From a84c32523cb971505e03ae73409839637d8706a9 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Sat, 2 Apr 2016 08:31:39 +0000 Subject: STM32 LLD renaming done. git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9208 35acf78f-673a-0410-8e92-d51de3d6d3f4 --- os/hal/ports/STM32/LLD/ADCv1/adc_lld.c | 336 ------ os/hal/ports/STM32/LLD/ADCv1/adc_lld.h | 441 -------- os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.c | 336 ++++++ os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h | 441 ++++++++ os/hal/ports/STM32/LLD/ADCv2/adc_lld.c | 428 -------- os/hal/ports/STM32/LLD/ADCv2/adc_lld.h | 570 ---------- os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.c | 428 ++++++++ os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.h | 570 ++++++++++ os/hal/ports/STM32/LLD/ADCv3/adc_lld.c | 938 ---------------- os/hal/ports/STM32/LLD/ADCv3/adc_lld.h | 888 --------------- os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.c | 938 ++++++++++++++++ os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.h | 888 +++++++++++++++ os/hal/ports/STM32/LLD/CANv1/can_lld.c | 847 -------------- os/hal/ports/STM32/LLD/CANv1/can_lld.h | 377 ------- os/hal/ports/STM32/LLD/CANv1/hal_can_lld.c | 847 ++++++++++++++ os/hal/ports/STM32/LLD/CANv1/hal_can_lld.h | 377 +++++++ os/hal/ports/STM32/LLD/DACv1/dac_lld.c | 515 --------- os/hal/ports/STM32/LLD/DACv1/dac_lld.h | 465 -------- os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c | 515 +++++++++ os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h | 465 ++++++++ os/hal/ports/STM32/LLD/EXTIv1/ext_lld.c | 239 ---- os/hal/ports/STM32/LLD/EXTIv1/ext_lld.h | 155 --- os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.c | 239 ++++ os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.h | 155 +++ os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.c | 183 ++++ os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.h | 376 +++++++ os/hal/ports/STM32/LLD/GPIOv1/pal_lld.c | 183 ---- os/hal/ports/STM32/LLD/GPIOv1/pal_lld.h | 376 ------- os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.c | 212 ++++ os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.h | 559 ++++++++++ os/hal/ports/STM32/LLD/GPIOv2/pal_lld.c | 212 ---- os/hal/ports/STM32/LLD/GPIOv2/pal_lld.h | 559 ---------- os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.c | 199 ++++ os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.h | 571 ++++++++++ os/hal/ports/STM32/LLD/GPIOv3/pal_lld.c | 199 ---- os/hal/ports/STM32/LLD/GPIOv3/pal_lld.h | 571 ---------- os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c | 855 +++++++++++++++ os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.h | 513 +++++++++ os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.c | 855 --------------- os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.h | 513 --------- os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c | 1156 +++++++++++++++++++ os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.h | 502 +++++++++ os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c | 1156 ------------------- os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h | 502 --------- os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.c | 740 +++++++++++++ os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.h | 363 ++++++ os/hal/ports/STM32/LLD/MACv1/mac_lld.c | 740 ------------- os/hal/ports/STM32/LLD/MACv1/mac_lld.h | 363 ------ os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.c | 1341 +++++++++++++++++++++++ os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.h | 556 ++++++++++ os/hal/ports/STM32/LLD/OTGv1/usb_lld.c | 1341 ----------------------- os/hal/ports/STM32/LLD/OTGv1/usb_lld.h | 556 ---------- os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.c | 443 ++++++++ os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.h | 198 ++++ os/hal/ports/STM32/LLD/RTCv1/rtc_lld.c | 443 -------- os/hal/ports/STM32/LLD/RTCv1/rtc_lld.h | 198 ---- os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.c | 548 +++++++++ os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.h | 237 ++++ os/hal/ports/STM32/LLD/RTCv2/rtc_lld.c | 548 --------- os/hal/ports/STM32/LLD/RTCv2/rtc_lld.h | 237 ---- os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.c | 873 +++++++++++++++ os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.h | 330 ++++++ os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.c | 873 --------------- os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.h | 330 ------ os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.c | 885 +++++++++++++++ os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.h | 275 +++++ os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.c | 885 --------------- os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.h | 275 ----- os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.c | 577 ++++++++++ os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.h | 432 ++++++++ os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.c | 633 +++++++++++ os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.h | 543 +++++++++ os/hal/ports/STM32/LLD/SPIv1/i2s_lld.c | 577 ---------- os/hal/ports/STM32/LLD/SPIv1/i2s_lld.h | 432 -------- os/hal/ports/STM32/LLD/SPIv1/spi_lld.c | 633 ----------- os/hal/ports/STM32/LLD/SPIv1/spi_lld.h | 543 --------- os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.c | 577 ++++++++++ os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.h | 432 ++++++++ os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.c | 652 +++++++++++ os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h | 547 +++++++++ os/hal/ports/STM32/LLD/SPIv2/i2s_lld.c | 577 ---------- os/hal/ports/STM32/LLD/SPIv2/i2s_lld.h | 432 -------- os/hal/ports/STM32/LLD/SPIv2/spi_lld.c | 652 ----------- os/hal/ports/STM32/LLD/SPIv2/spi_lld.h | 547 --------- os/hal/ports/STM32/LLD/TIMv1/gpt_lld.c | 891 --------------- os/hal/ports/STM32/LLD/TIMv1/gpt_lld.h | 641 ----------- os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.c | 891 +++++++++++++++ os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.h | 641 +++++++++++ os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.c | 805 ++++++++++++++ os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.h | 487 ++++++++ os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.c | 858 +++++++++++++++ os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.h | 550 ++++++++++ os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.c | 308 ++++++ os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.h | 210 ++++ os/hal/ports/STM32/LLD/TIMv1/icu_lld.c | 805 -------------- os/hal/ports/STM32/LLD/TIMv1/icu_lld.h | 487 -------- os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c | 858 --------------- os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h | 550 ---------- os/hal/ports/STM32/LLD/TIMv1/st_lld.c | 308 ------ os/hal/ports/STM32/LLD/TIMv1/st_lld.h | 210 ---- os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.c | 631 +++++++++++ os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.h | 360 ++++++ os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c | 811 ++++++++++++++ os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.h | 623 +++++++++++ os/hal/ports/STM32/LLD/USARTv1/serial_lld.c | 631 ----------- os/hal/ports/STM32/LLD/USARTv1/serial_lld.h | 360 ------ os/hal/ports/STM32/LLD/USARTv1/uart_lld.c | 811 -------------- os/hal/ports/STM32/LLD/USARTv1/uart_lld.h | 623 ----------- os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.c | 764 +++++++++++++ os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.h | 399 +++++++ os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.c | 931 ++++++++++++++++ os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.h | 734 +++++++++++++ os/hal/ports/STM32/LLD/USARTv2/serial_lld.c | 764 ------------- os/hal/ports/STM32/LLD/USARTv2/serial_lld.h | 399 ------- os/hal/ports/STM32/LLD/USARTv2/uart_lld.c | 931 ---------------- os/hal/ports/STM32/LLD/USARTv2/uart_lld.h | 734 ------------- os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.c | 863 +++++++++++++++ os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.h | 482 ++++++++ os/hal/ports/STM32/LLD/USBv1/usb_lld.c | 863 --------------- os/hal/ports/STM32/LLD/USBv1/usb_lld.h | 482 -------- os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.c | 140 +++ os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.h | 183 ++++ os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.c | 140 --- os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.h | 183 ---- 124 files changed, 34178 insertions(+), 34178 deletions(-) delete mode 100644 os/hal/ports/STM32/LLD/ADCv1/adc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/ADCv1/adc_lld.h create mode 100644 os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.c create mode 100644 os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/ADCv2/adc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/ADCv2/adc_lld.h create mode 100644 os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.c create mode 100644 os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/ADCv3/adc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/ADCv3/adc_lld.h create mode 100644 os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.c create mode 100644 os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/CANv1/can_lld.c delete mode 100644 os/hal/ports/STM32/LLD/CANv1/can_lld.h create mode 100644 os/hal/ports/STM32/LLD/CANv1/hal_can_lld.c create mode 100644 os/hal/ports/STM32/LLD/CANv1/hal_can_lld.h delete mode 100644 os/hal/ports/STM32/LLD/DACv1/dac_lld.c delete mode 100644 os/hal/ports/STM32/LLD/DACv1/dac_lld.h create mode 100644 os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c create mode 100644 os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h delete mode 100644 os/hal/ports/STM32/LLD/EXTIv1/ext_lld.c delete mode 100644 os/hal/ports/STM32/LLD/EXTIv1/ext_lld.h create mode 100644 os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.c create mode 100644 os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.h create mode 100644 os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.c create mode 100644 os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.h delete mode 100644 os/hal/ports/STM32/LLD/GPIOv1/pal_lld.c delete mode 100644 os/hal/ports/STM32/LLD/GPIOv1/pal_lld.h create mode 100644 os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.c create mode 100644 os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.h delete mode 100644 os/hal/ports/STM32/LLD/GPIOv2/pal_lld.c delete mode 100644 os/hal/ports/STM32/LLD/GPIOv2/pal_lld.h create mode 100644 os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.c create mode 100644 os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.h delete mode 100644 os/hal/ports/STM32/LLD/GPIOv3/pal_lld.c delete mode 100644 os/hal/ports/STM32/LLD/GPIOv3/pal_lld.h create mode 100644 os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c create mode 100644 os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.h delete mode 100644 os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.c delete mode 100644 os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.h create mode 100644 os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c create mode 100644 os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.h delete mode 100644 os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c delete mode 100644 os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h create mode 100644 os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.c create mode 100644 os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.h delete mode 100644 os/hal/ports/STM32/LLD/MACv1/mac_lld.c delete mode 100644 os/hal/ports/STM32/LLD/MACv1/mac_lld.h create mode 100644 os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.c create mode 100644 os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.h delete mode 100644 os/hal/ports/STM32/LLD/OTGv1/usb_lld.c delete mode 100644 os/hal/ports/STM32/LLD/OTGv1/usb_lld.h create mode 100644 os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.c create mode 100644 os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/RTCv1/rtc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/RTCv1/rtc_lld.h create mode 100644 os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.c create mode 100644 os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/RTCv2/rtc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/RTCv2/rtc_lld.h create mode 100644 os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.c create mode 100644 os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.h create mode 100644 os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.c create mode 100644 os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.h delete mode 100644 os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.c delete mode 100644 os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.h create mode 100644 os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.c create mode 100644 os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.h create mode 100644 os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.c create mode 100644 os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.h delete mode 100644 os/hal/ports/STM32/LLD/SPIv1/i2s_lld.c delete mode 100644 os/hal/ports/STM32/LLD/SPIv1/i2s_lld.h delete mode 100644 os/hal/ports/STM32/LLD/SPIv1/spi_lld.c delete mode 100644 os/hal/ports/STM32/LLD/SPIv1/spi_lld.h create mode 100644 os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.c create mode 100644 os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.h create mode 100644 os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.c create mode 100644 os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h delete mode 100644 os/hal/ports/STM32/LLD/SPIv2/i2s_lld.c delete mode 100644 os/hal/ports/STM32/LLD/SPIv2/i2s_lld.h delete mode 100644 os/hal/ports/STM32/LLD/SPIv2/spi_lld.c delete mode 100644 os/hal/ports/STM32/LLD/SPIv2/spi_lld.h delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/gpt_lld.c delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/gpt_lld.h create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.c create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.h create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.c create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.h create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.c create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.h create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.c create mode 100644 os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.h delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/icu_lld.c delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/icu_lld.h delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/st_lld.c delete mode 100644 os/hal/ports/STM32/LLD/TIMv1/st_lld.h create mode 100644 os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.c create mode 100644 os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.h create mode 100644 os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c create mode 100644 os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.h delete mode 100644 os/hal/ports/STM32/LLD/USARTv1/serial_lld.c delete mode 100644 os/hal/ports/STM32/LLD/USARTv1/serial_lld.h delete mode 100644 os/hal/ports/STM32/LLD/USARTv1/uart_lld.c delete mode 100644 os/hal/ports/STM32/LLD/USARTv1/uart_lld.h create mode 100644 os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.c create mode 100644 os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.h create mode 100644 os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.c create mode 100644 os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.h delete mode 100644 os/hal/ports/STM32/LLD/USARTv2/serial_lld.c delete mode 100644 os/hal/ports/STM32/LLD/USARTv2/serial_lld.h delete mode 100644 os/hal/ports/STM32/LLD/USARTv2/uart_lld.c delete mode 100644 os/hal/ports/STM32/LLD/USARTv2/uart_lld.h create mode 100644 os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.c create mode 100644 os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.h delete mode 100644 os/hal/ports/STM32/LLD/USBv1/usb_lld.c delete mode 100644 os/hal/ports/STM32/LLD/USBv1/usb_lld.h create mode 100644 os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.c create mode 100644 os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.h delete mode 100644 os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.c delete mode 100644 os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.h diff --git a/os/hal/ports/STM32/LLD/ADCv1/adc_lld.c b/os/hal/ports/STM32/LLD/ADCv1/adc_lld.c deleted file mode 100644 index 45969e18c..000000000 --- a/os/hal/ports/STM32/LLD/ADCv1/adc_lld.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/LLD/ADCv1/adc_lld.c - * @brief STM32 ADC subsystem low level driver source. - * - * @addtogroup ADC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define ADC1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief ADC1 driver identifier.*/ -#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) -ADCDriver ADCD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Stops an ongoing conversion, if any. - * - * @param[in] adc pointer to the ADC registers block - */ -static void adc_lld_stop_adc(ADC_TypeDef *adc) { - - if (adc->CR & ADC_CR_ADSTART) { - adc->CR |= ADC_CR_ADSTP; - while (adc->CR & ADC_CR_ADSTP) - ; - } -} - -/** - * @brief ADC DMA ISR service routine. - * - * @param[in] adcp pointer to the @p ADCDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) { - - /* DMA errors handling.*/ - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - /* DMA, this could help only if the DMA tries to access an unmapped - address space or violates alignment rules.*/ - _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); - } - else { - /* It is possible that the conversion group has already be reset by the - ADC error handler, in this case this interrupt is spurious.*/ - if (adcp->grpp != NULL) { - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _adc_isr_full_code(adcp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _adc_isr_half_code(adcp); - } - } - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if (STM32_ADC_USE_ADC1 && (STM32_ADC1_IRQ_SHARED_WITH_EXTI == FALSE)) || \ - defined(__DOXYGEN__) -#if !defined(STM32_ADC1_HANDLER) -#error "STM32_ADC1_HANDLER not defined" -#endif -/** - * @brief ADC interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - adc_lld_serve_interrupt(&ADCD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ADC driver initialization. - * - * @notapi - */ -void adc_lld_init(void) { - -#if STM32_ADC_USE_ADC1 - /* Driver initialization.*/ - adcObjectInit(&ADCD1); - ADCD1.adc = ADC1; - ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); - ADCD1.dmamode = STM32_DMA_CR_CHSEL(ADC1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - -#if STM32_ADC1_IRQ_SHARED_WITH_EXTI == FALSE - /* The shared vector is initialized on driver initialization and never - disabled.*/ - nvicEnableVector(12, STM32_ADC_ADC1_IRQ_PRIORITY); -#endif -#endif - - /* Calibration procedure.*/ - rccEnableADC1(FALSE); - - /* CCR setup.*/ -#if STM32_ADC_SUPPORTS_PRESCALER - ADC->CCR = STM32_ADC_PRESC << 18; -#else - ADC->CCR = 0; -#endif - - osalDbgAssert(ADC1->CR == 0, "invalid register state"); - ADC1->CR |= ADC_CR_ADCAL; - osalDbgAssert(ADC1->CR != 0, "invalid register state"); - while (ADC1->CR & ADC_CR_ADCAL) - ; - rccDisableADC1(FALSE); -} - -/** - * @brief Configures and activates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start(ADCDriver *adcp) { - - /* If in stopped state then enables the ADC and DMA clocks.*/ - if (adcp->state == ADC_STOP) { -#if STM32_ADC_USE_ADC1 - if (&ADCD1 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC1_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR); - rccEnableADC1(FALSE); - - /* Clock settings.*/ - adcp->adc->CFGR2 = STM32_ADC_ADC1_CKMODE; - } -#endif /* STM32_ADC_USE_ADC1 */ - - /* ADC initial setup, starting the analog part here in order to reduce - the latency when starting a conversion.*/ - adcp->adc->CR = ADC_CR_ADEN; - while (!(adcp->adc->ISR & ADC_ISR_ADRDY)) - ; - } -} - -/** - * @brief Deactivates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop(ADCDriver *adcp) { - - /* If in ready state then disables the ADC clock and analog part.*/ - if (adcp->state == ADC_READY) { - - dmaStreamRelease(adcp->dmastp); - - /* Restoring CCR default.*/ -#if STM32_ADC_SUPPORTS_PRESCALER - ADC->CCR = STM32_ADC_PRESC << 18; -#else - ADC->CCR = 0; -#endif - - /* Disabling ADC.*/ - if (adcp->adc->CR & ADC_CR_ADEN) { - adc_lld_stop_adc(adcp->adc); - adcp->adc->CR |= ADC_CR_ADDIS; - while (adcp->adc->CR & ADC_CR_ADDIS) - ; - } - -#if STM32_ADC_USE_ADC1 - if (&ADCD1 == adcp) - rccDisableADC1(FALSE); -#endif - } -} - -/** - * @brief Starts an ADC conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start_conversion(ADCDriver *adcp) { - uint32_t mode, cfgr1; - const ADCConversionGroup *grpp = adcp->grpp; - - /* DMA setup.*/ - mode = adcp->dmamode; - cfgr1 = grpp->cfgr1 | ADC_CFGR1_DMAEN; - if (grpp->circular) { - mode |= STM32_DMA_CR_CIRC; - cfgr1 |= ADC_CFGR1_DMACFG; - if (adcp->depth > 1) { - /* If circular buffer depth > 1, then the half transfer interrupt - is enabled in order to allow streaming processing.*/ - mode |= STM32_DMA_CR_HTIE; - } - } - dmaStreamSetMemory0(adcp->dmastp, adcp->samples); - dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * - (uint32_t)adcp->depth); - dmaStreamSetMode(adcp->dmastp, mode); - dmaStreamEnable(adcp->dmastp); - - /* ADC setup, if it is defined a callback for the analog watch dog then it - is enabled.*/ - adcp->adc->ISR = adcp->adc->ISR; - adcp->adc->IER = ADC_IER_OVRIE | ADC_IER_AWDIE; - adcp->adc->TR = grpp->tr; - adcp->adc->SMPR = grpp->smpr; - adcp->adc->CHSELR = grpp->chselr; - - /* ADC configuration and start.*/ - adcp->adc->CFGR1 = cfgr1; -#if STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE - { - uint32_t cfgr2 = adcp->adc->CFGR2 & STM32_ADC_CKMODE_MASK; - adcp->adc->CFGR2 = cfgr2 | grpp->cfgr2; - } -#endif - - /* ADC conversion start.*/ - adcp->adc->CR |= ADC_CR_ADSTART; -} - -/** - * @brief Stops an ongoing conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop_conversion(ADCDriver *adcp) { - - dmaStreamDisable(adcp->dmastp); - adc_lld_stop_adc(adcp->adc); -} - -/** - * @brief ISR code. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_serve_interrupt(ADCDriver *adcp) { - uint32_t isr; - - isr = adcp->adc->ISR; - adcp->adc->ISR = isr; - - /* It could be a spurious interrupt caused by overflows after DMA disabling, - just ignore it in this case.*/ - if (adcp->grpp != NULL) { - /* Note, an overflow may occur after the conversion ended before the driver - is able to stop the ADC, this is why the DMA channel is checked too.*/ - if ((isr & ADC_ISR_OVR) && - (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) { - /* ADC overflow condition, this could happen only if the DMA is unable - to read data fast enough.*/ - _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW); - } - if (isr & ADC_ISR_AWD) { - /* Analog watchdog error.*/ - _adc_isr_error_code(adcp, ADC_ERR_AWD); - } - } -} - -#endif /* HAL_USE_ADC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv1/adc_lld.h b/os/hal/ports/STM32/LLD/ADCv1/adc_lld.h deleted file mode 100644 index ced2ee8b5..000000000 --- a/os/hal/ports/STM32/LLD/ADCv1/adc_lld.h +++ /dev/null @@ -1,441 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/LLD/ADCv1/adc_lld.h - * @brief STM32 ADC subsystem low level driver header. - * - * @addtogroup ADC - * @{ - */ - -#ifndef _ADC_LLD_H_ -#define _ADC_LLD_H_ - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Sampling rates - * @{ - */ -#define ADC_SMPR_SMP_1P5 0U /**< @brief 14 cycles conversion time */ -#define ADC_SMPR_SMP_7P5 1U /**< @brief 21 cycles conversion time. */ -#define ADC_SMPR_SMP_13P5 2U /**< @brief 28 cycles conversion time. */ -#define ADC_SMPR_SMP_28P5 3U /**< @brief 41 cycles conversion time. */ -#define ADC_SMPR_SMP_41P5 4U /**< @brief 54 cycles conversion time. */ -#define ADC_SMPR_SMP_55P5 5U /**< @brief 68 cycles conversion time. */ -#define ADC_SMPR_SMP_71P5 6U /**< @brief 84 cycles conversion time. */ -#define ADC_SMPR_SMP_239P5 7U /**< @brief 252 cycles conversion time. */ -/** @} */ - -/** - * @name CFGR1 register configuration helpers - * @{ - */ -#define ADC_CFGR1_RES_12BIT (0U << 3U) -#define ADC_CFGR1_RES_10BIT (1U << 3U) -#define ADC_CFGR1_RES_8BIT (2U << 3U) -#define ADC_CFGR1_RES_6BIT (3U << 3U) - -#define ADC_CFGR1_EXTSEL_MASK (15U << 6U) -#define ADC_CFGR1_EXTSEL_SRC(n) ((n) << 6U) - -#define ADC_CFGR1_EXTEN_MASK (3U << 10U) -#define ADC_CFGR1_EXTEN_DISABLED (0U << 10U) -#define ADC_CFGR1_EXTEN_RISING (1U << 10U) -#define ADC_CFGR1_EXTEN_FALLING (2U << 10U) -#define ADC_CFGR1_EXTEN_BOTH (3U << 10U) -/** @} */ - -/** - * @name CFGR2 register configuration helpers - * @{ - */ -#define STM32_ADC_CKMODE_MASK (3U << 30U) -#define STM32_ADC_CKMODE_ADCCLK (0U << 30U) -#define STM32_ADC_CKMODE_PCLK_DIV2 (1U << 30U) -#define STM32_ADC_CKMODE_PCLK_DIV4 (2U << 30U) -#define STM32_ADC_CKMODE_PCLK (3U << 30U) - -#if (STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE) || defined(__DOXYGEN__) -#define ADC_CFGR2_OVSR_MASK (7U << 2U) -#define ADC_CFGR2_OVSR_2X (0U << 2U) -#define ADC_CFGR2_OVSR_4X (1U << 2U) -#define ADC_CFGR2_OVSR_8X (2U << 2U) -#define ADC_CFGR2_OVSR_16X (3U << 2U) -#define ADC_CFGR2_OVSR_32X (4U << 2U) -#define ADC_CFGR2_OVSR_64X (5U << 2U) -#define ADC_CFGR2_OVSR_128X (6U << 2U) -#define ADC_CFGR2_OVSR_256X (7U << 2U) - -#define ADC_CFGR2_OVSS_MASK (15 << 5U) -#define ADC_CFGR2_OVSS_SHIFT(n) ((n) << 5U) -#endif -/** @} */ - -/** - * @name Threashold register initializer - * @{ - */ -#define ADC_TR(low, high) (((uint32_t)(high) << 16U) | \ - (uint32_t)(low)) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief ADC1 driver enable switch. - * @details If set to @p TRUE the support for ADC1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC1 FALSE -#endif - -/** - * @brief ADC1 clock source selection. - */ -#if !defined(STM32_ADC_ADC1_CKMODE) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_CKMODE STM32_ADC_CKMODE_ADCCLK -#endif - -/** - * @brief ADC1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_IRQ_PRIORITY 2 -#endif - -/** - * @brief ADC1 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2 -#endif - -#if (STM32_ADC_SUPPORTS_PRESCALER == TRUE) || defined(__DOXYGEN__) -/* - * @brief ADC prescaler setting. - * @note This setting has effect only in asynchronous clock mode (the - * default, @p STM32_ADC_CKMODE_ADCCLK). - */ -#if !defined(STM32_ADC_PRESCALER_VALUE) || defined(__DOXYGEN__) -#define STM32_ADC_PRESCALER_VALUE 1 -#endif -#endif - -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 && !STM32_HAS_ADC1 -#error "ADC1 not present in the selected device" -#endif - -#if !STM32_ADC_USE_ADC1 -#error "ADC driver activated but no ADC peripheral assigned" -#endif - -#if STM32_ADC1_IRQ_SHARED_WITH_EXTI == FALSE -#if STM32_ADC_USE_ADC1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC1" -#endif -#endif - -#if STM32_ADC_USE_ADC1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC1 DMA" -#endif - -#if STM32_ADC_USE_ADC1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to ADC1" -#endif - -#if STM32_ADC_SUPPORTS_PRESCALER == TRUE -#if STM32_ADC_PRESCALER_VALUE == 1 -#define STM32_ADC_PRESC 0U -#elif STM32_ADC_PRESCALER_VALUE == 2 -#define STM32_ADC_PRESC 1U -#elif STM32_ADC_PRESCALER_VALUE == 4 -#define STM32_ADC_PRESC 2U -#elif STM32_ADC_PRESCALER_VALUE == 6 -#define STM32_ADC_PRESC 3U -#elif STM32_ADC_PRESCALER_VALUE == 8 -#define STM32_ADC_PRESC 4U -#elif STM32_ADC_PRESCALER_VALUE == 10 -#define STM32_ADC_PRESC 5U -#elif STM32_ADC_PRESCALER_VALUE == 12 -#define STM32_ADC_PRESC 6U -#elif STM32_ADC_PRESCALER_VALUE == 16 -#define STM32_ADC_PRESC 7U -#elif STM32_ADC_PRESCALER_VALUE == 32 -#define STM32_ADC_PRESC 8U -#elif STM32_ADC_PRESCALER_VALUE == 64 -#define STM32_ADC_PRESC 9U -#elif STM32_ADC_PRESCALER_VALUE == 128 -#define STM32_ADC_PRESC 10U -#elif STM32_ADC_PRESCALER_VALUE == 256 -#define STM32_ADC_PRESC 11U -#else -#error "Invalid value assigned to STM32_ADC_PRESCALER_VALUE" -#endif -#endif - -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_ADC_USE_ADC1 && !defined(STM32_ADC_ADC1_DMA_STREAM) -#error "ADC DMA stream not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_ADC_USE_ADC1 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK) -#error "invalid DMA stream associated to ADC1" -#endif - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief ADC sample data type. - */ -typedef uint16_t adcsample_t; - -/** - * @brief Channels number in a conversion group. - */ -typedef uint16_t adc_channels_num_t; - -/** - * @brief Possible ADC failure causes. - * @note Error codes are architecture dependent and should not relied - * upon. - */ -typedef enum { - ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ - ADC_ERR_OVERFLOW = 1, /**< ADC overflow condition. */ - ADC_ERR_AWD = 2 /**< Analog watchdog triggered. */ -} adcerror_t; - -/** - * @brief Type of a structure representing an ADC driver. - */ -typedef struct ADCDriver ADCDriver; - -/** - * @brief ADC notification callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] buffer pointer to the most recent samples data - * @param[in] n number of buffer rows available starting from @p buffer - */ -typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); - -/** - * @brief ADC error callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] err ADC error code - */ -typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); - -/** - * @brief Conversion group configuration structure. - * @details This implementation-dependent structure describes a conversion - * operation. - * @note The use of this configuration structure requires knowledge of - * STM32 ADC cell registers interface, please refer to the STM32 - * reference manual for details. - */ -typedef struct { - /** - * @brief Enables the circular buffer mode for the group. - */ - bool circular; - /** - * @brief Number of the analog channels belonging to the conversion group. - */ - adc_channels_num_t num_channels; - /** - * @brief Callback function associated to the group or @p NULL. - */ - adccallback_t end_cb; - /** - * @brief Error callback or @p NULL. - */ - adcerrorcallback_t error_cb; - /* End of the mandatory fields.*/ - /** - * @brief ADC CFGR1 register initialization data. - * @note The bits DMAEN and DMACFG are enforced internally - * to the driver, keep them to zero. - * @note The bits @p ADC_CFGR1_CONT or @p ADC_CFGR1_DISCEN must be - * specified in continuous more or if the buffer depth is - * greater than one. - */ - uint32_t cfgr1; -#if (STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE) || defined(__DOXYGEN__) - /** - * @brief ADC CFGR2 register initialization data. - * @note CKMODE bits must not be specified in this field and left to - * zero. - */ - uint32_t cfgr2; -#endif - /** - * @brief ADC TR register initialization data. - */ - uint32_t tr; - /** - * @brief ADC SMPR register initialization data. - */ - uint32_t smpr; - /** - * @brief ADC CHSELR register initialization data. - * @details The number of bits at logic level one in this register must - * be equal to the number in the @p num_channels field. - */ - uint32_t chselr; -} ADCConversionGroup; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - uint32_t dummy; -} ADCConfig; - -/** - * @brief Structure representing an ADC driver. - */ -struct ADCDriver { - /** - * @brief Driver state. - */ - adcstate_t state; - /** - * @brief Current configuration data. - */ - const ADCConfig *config; - /** - * @brief Current samples buffer pointer or @p NULL. - */ - adcsample_t *samples; - /** - * @brief Current samples buffer depth or @p 0. - */ - size_t depth; - /** - * @brief Current conversion group pointer or @p NULL. - */ - const ADCConversionGroup *grpp; -#if ADC_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif -#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* ADC_USE_MUTUAL_EXCLUSION */ -#if defined(ADC_DRIVER_EXT_FIELDS) - ADC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the ADCx registers block. - */ - ADC_TypeDef *adc; - /** - * @brief Pointer to associated DMA channel. - */ - const stm32_dma_stream_t *dmastp; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Changes the value of the ADC CCR register. - * @details Use this function in order to enable or disable the internal - * analog sources. See the documentation in the STM32 Reference - * Manual. - * @note PRESC bits must not be specified and left to zero. - */ -#define adcSTM32SetCCR(ccr) (ADC->CCR = (ccr)) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 && !defined(__DOXYGEN__) -extern ADCDriver ADCD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void adc_lld_init(void); - void adc_lld_start(ADCDriver *adcp); - void adc_lld_stop(ADCDriver *adcp); - void adc_lld_start_conversion(ADCDriver *adcp); - void adc_lld_stop_conversion(ADCDriver *adcp); - void adc_lld_serve_interrupt(ADCDriver *adcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_ADC */ - -#endif /* _ADC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.c b/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.c new file mode 100644 index 000000000..45969e18c --- /dev/null +++ b/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.c @@ -0,0 +1,336 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/LLD/ADCv1/adc_lld.c + * @brief STM32 ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define ADC1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief ADC1 driver identifier.*/ +#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Stops an ongoing conversion, if any. + * + * @param[in] adc pointer to the ADC registers block + */ +static void adc_lld_stop_adc(ADC_TypeDef *adc) { + + if (adc->CR & ADC_CR_ADSTART) { + adc->CR |= ADC_CR_ADSTP; + while (adc->CR & ADC_CR_ADSTP) + ; + } +} + +/** + * @brief ADC DMA ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) { + + /* DMA errors handling.*/ + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + /* DMA, this could help only if the DMA tries to access an unmapped + address space or violates alignment rules.*/ + _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); + } + else { + /* It is possible that the conversion group has already be reset by the + ADC error handler, in this case this interrupt is spurious.*/ + if (adcp->grpp != NULL) { + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _adc_isr_full_code(adcp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _adc_isr_half_code(adcp); + } + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if (STM32_ADC_USE_ADC1 && (STM32_ADC1_IRQ_SHARED_WITH_EXTI == FALSE)) || \ + defined(__DOXYGEN__) +#if !defined(STM32_ADC1_HANDLER) +#error "STM32_ADC1_HANDLER not defined" +#endif +/** + * @brief ADC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + adc_lld_serve_interrupt(&ADCD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + +#if STM32_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); + ADCD1.adc = ADC1; + ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); + ADCD1.dmamode = STM32_DMA_CR_CHSEL(ADC1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + +#if STM32_ADC1_IRQ_SHARED_WITH_EXTI == FALSE + /* The shared vector is initialized on driver initialization and never + disabled.*/ + nvicEnableVector(12, STM32_ADC_ADC1_IRQ_PRIORITY); +#endif +#endif + + /* Calibration procedure.*/ + rccEnableADC1(FALSE); + + /* CCR setup.*/ +#if STM32_ADC_SUPPORTS_PRESCALER + ADC->CCR = STM32_ADC_PRESC << 18; +#else + ADC->CCR = 0; +#endif + + osalDbgAssert(ADC1->CR == 0, "invalid register state"); + ADC1->CR |= ADC_CR_ADCAL; + osalDbgAssert(ADC1->CR != 0, "invalid register state"); + while (ADC1->CR & ADC_CR_ADCAL) + ; + rccDisableADC1(FALSE); +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* If in stopped state then enables the ADC and DMA clocks.*/ + if (adcp->state == ADC_STOP) { +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC1_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR); + rccEnableADC1(FALSE); + + /* Clock settings.*/ + adcp->adc->CFGR2 = STM32_ADC_ADC1_CKMODE; + } +#endif /* STM32_ADC_USE_ADC1 */ + + /* ADC initial setup, starting the analog part here in order to reduce + the latency when starting a conversion.*/ + adcp->adc->CR = ADC_CR_ADEN; + while (!(adcp->adc->ISR & ADC_ISR_ADRDY)) + ; + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock and analog part.*/ + if (adcp->state == ADC_READY) { + + dmaStreamRelease(adcp->dmastp); + + /* Restoring CCR default.*/ +#if STM32_ADC_SUPPORTS_PRESCALER + ADC->CCR = STM32_ADC_PRESC << 18; +#else + ADC->CCR = 0; +#endif + + /* Disabling ADC.*/ + if (adcp->adc->CR & ADC_CR_ADEN) { + adc_lld_stop_adc(adcp->adc); + adcp->adc->CR |= ADC_CR_ADDIS; + while (adcp->adc->CR & ADC_CR_ADDIS) + ; + } + +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) + rccDisableADC1(FALSE); +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + uint32_t mode, cfgr1; + const ADCConversionGroup *grpp = adcp->grpp; + + /* DMA setup.*/ + mode = adcp->dmamode; + cfgr1 = grpp->cfgr1 | ADC_CFGR1_DMAEN; + if (grpp->circular) { + mode |= STM32_DMA_CR_CIRC; + cfgr1 |= ADC_CFGR1_DMACFG; + if (adcp->depth > 1) { + /* If circular buffer depth > 1, then the half transfer interrupt + is enabled in order to allow streaming processing.*/ + mode |= STM32_DMA_CR_HTIE; + } + } + dmaStreamSetMemory0(adcp->dmastp, adcp->samples); + dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * + (uint32_t)adcp->depth); + dmaStreamSetMode(adcp->dmastp, mode); + dmaStreamEnable(adcp->dmastp); + + /* ADC setup, if it is defined a callback for the analog watch dog then it + is enabled.*/ + adcp->adc->ISR = adcp->adc->ISR; + adcp->adc->IER = ADC_IER_OVRIE | ADC_IER_AWDIE; + adcp->adc->TR = grpp->tr; + adcp->adc->SMPR = grpp->smpr; + adcp->adc->CHSELR = grpp->chselr; + + /* ADC configuration and start.*/ + adcp->adc->CFGR1 = cfgr1; +#if STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE + { + uint32_t cfgr2 = adcp->adc->CFGR2 & STM32_ADC_CKMODE_MASK; + adcp->adc->CFGR2 = cfgr2 | grpp->cfgr2; + } +#endif + + /* ADC conversion start.*/ + adcp->adc->CR |= ADC_CR_ADSTART; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + + dmaStreamDisable(adcp->dmastp); + adc_lld_stop_adc(adcp->adc); +} + +/** + * @brief ISR code. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_serve_interrupt(ADCDriver *adcp) { + uint32_t isr; + + isr = adcp->adc->ISR; + adcp->adc->ISR = isr; + + /* It could be a spurious interrupt caused by overflows after DMA disabling, + just ignore it in this case.*/ + if (adcp->grpp != NULL) { + /* Note, an overflow may occur after the conversion ended before the driver + is able to stop the ADC, this is why the DMA channel is checked too.*/ + if ((isr & ADC_ISR_OVR) && + (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) { + /* ADC overflow condition, this could happen only if the DMA is unable + to read data fast enough.*/ + _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW); + } + if (isr & ADC_ISR_AWD) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD); + } + } +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h b/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h new file mode 100644 index 000000000..ced2ee8b5 --- /dev/null +++ b/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h @@ -0,0 +1,441 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/LLD/ADCv1/adc_lld.h + * @brief STM32 ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef _ADC_LLD_H_ +#define _ADC_LLD_H_ + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Sampling rates + * @{ + */ +#define ADC_SMPR_SMP_1P5 0U /**< @brief 14 cycles conversion time */ +#define ADC_SMPR_SMP_7P5 1U /**< @brief 21 cycles conversion time. */ +#define ADC_SMPR_SMP_13P5 2U /**< @brief 28 cycles conversion time. */ +#define ADC_SMPR_SMP_28P5 3U /**< @brief 41 cycles conversion time. */ +#define ADC_SMPR_SMP_41P5 4U /**< @brief 54 cycles conversion time. */ +#define ADC_SMPR_SMP_55P5 5U /**< @brief 68 cycles conversion time. */ +#define ADC_SMPR_SMP_71P5 6U /**< @brief 84 cycles conversion time. */ +#define ADC_SMPR_SMP_239P5 7U /**< @brief 252 cycles conversion time. */ +/** @} */ + +/** + * @name CFGR1 register configuration helpers + * @{ + */ +#define ADC_CFGR1_RES_12BIT (0U << 3U) +#define ADC_CFGR1_RES_10BIT (1U << 3U) +#define ADC_CFGR1_RES_8BIT (2U << 3U) +#define ADC_CFGR1_RES_6BIT (3U << 3U) + +#define ADC_CFGR1_EXTSEL_MASK (15U << 6U) +#define ADC_CFGR1_EXTSEL_SRC(n) ((n) << 6U) + +#define ADC_CFGR1_EXTEN_MASK (3U << 10U) +#define ADC_CFGR1_EXTEN_DISABLED (0U << 10U) +#define ADC_CFGR1_EXTEN_RISING (1U << 10U) +#define ADC_CFGR1_EXTEN_FALLING (2U << 10U) +#define ADC_CFGR1_EXTEN_BOTH (3U << 10U) +/** @} */ + +/** + * @name CFGR2 register configuration helpers + * @{ + */ +#define STM32_ADC_CKMODE_MASK (3U << 30U) +#define STM32_ADC_CKMODE_ADCCLK (0U << 30U) +#define STM32_ADC_CKMODE_PCLK_DIV2 (1U << 30U) +#define STM32_ADC_CKMODE_PCLK_DIV4 (2U << 30U) +#define STM32_ADC_CKMODE_PCLK (3U << 30U) + +#if (STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE) || defined(__DOXYGEN__) +#define ADC_CFGR2_OVSR_MASK (7U << 2U) +#define ADC_CFGR2_OVSR_2X (0U << 2U) +#define ADC_CFGR2_OVSR_4X (1U << 2U) +#define ADC_CFGR2_OVSR_8X (2U << 2U) +#define ADC_CFGR2_OVSR_16X (3U << 2U) +#define ADC_CFGR2_OVSR_32X (4U << 2U) +#define ADC_CFGR2_OVSR_64X (5U << 2U) +#define ADC_CFGR2_OVSR_128X (6U << 2U) +#define ADC_CFGR2_OVSR_256X (7U << 2U) + +#define ADC_CFGR2_OVSS_MASK (15 << 5U) +#define ADC_CFGR2_OVSS_SHIFT(n) ((n) << 5U) +#endif +/** @} */ + +/** + * @name Threashold register initializer + * @{ + */ +#define ADC_TR(low, high) (((uint32_t)(high) << 16U) | \ + (uint32_t)(low)) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief ADC1 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC1 FALSE +#endif + +/** + * @brief ADC1 clock source selection. + */ +#if !defined(STM32_ADC_ADC1_CKMODE) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_CKMODE STM32_ADC_CKMODE_ADCCLK +#endif + +/** + * @brief ADC1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_IRQ_PRIORITY 2 +#endif + +/** + * @brief ADC1 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2 +#endif + +#if (STM32_ADC_SUPPORTS_PRESCALER == TRUE) || defined(__DOXYGEN__) +/* + * @brief ADC prescaler setting. + * @note This setting has effect only in asynchronous clock mode (the + * default, @p STM32_ADC_CKMODE_ADCCLK). + */ +#if !defined(STM32_ADC_PRESCALER_VALUE) || defined(__DOXYGEN__) +#define STM32_ADC_PRESCALER_VALUE 1 +#endif +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 && !STM32_HAS_ADC1 +#error "ADC1 not present in the selected device" +#endif + +#if !STM32_ADC_USE_ADC1 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +#if STM32_ADC1_IRQ_SHARED_WITH_EXTI == FALSE +#if STM32_ADC_USE_ADC1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC1" +#endif +#endif + +#if STM32_ADC_USE_ADC1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC1 DMA" +#endif + +#if STM32_ADC_USE_ADC1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to ADC1" +#endif + +#if STM32_ADC_SUPPORTS_PRESCALER == TRUE +#if STM32_ADC_PRESCALER_VALUE == 1 +#define STM32_ADC_PRESC 0U +#elif STM32_ADC_PRESCALER_VALUE == 2 +#define STM32_ADC_PRESC 1U +#elif STM32_ADC_PRESCALER_VALUE == 4 +#define STM32_ADC_PRESC 2U +#elif STM32_ADC_PRESCALER_VALUE == 6 +#define STM32_ADC_PRESC 3U +#elif STM32_ADC_PRESCALER_VALUE == 8 +#define STM32_ADC_PRESC 4U +#elif STM32_ADC_PRESCALER_VALUE == 10 +#define STM32_ADC_PRESC 5U +#elif STM32_ADC_PRESCALER_VALUE == 12 +#define STM32_ADC_PRESC 6U +#elif STM32_ADC_PRESCALER_VALUE == 16 +#define STM32_ADC_PRESC 7U +#elif STM32_ADC_PRESCALER_VALUE == 32 +#define STM32_ADC_PRESC 8U +#elif STM32_ADC_PRESCALER_VALUE == 64 +#define STM32_ADC_PRESC 9U +#elif STM32_ADC_PRESCALER_VALUE == 128 +#define STM32_ADC_PRESC 10U +#elif STM32_ADC_PRESCALER_VALUE == 256 +#define STM32_ADC_PRESC 11U +#else +#error "Invalid value assigned to STM32_ADC_PRESCALER_VALUE" +#endif +#endif + +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_ADC_USE_ADC1 && !defined(STM32_ADC_ADC1_DMA_STREAM) +#error "ADC DMA stream not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_ADC_USE_ADC1 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK) +#error "invalid DMA stream associated to ADC1" +#endif + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +typedef uint16_t adcsample_t; + +/** + * @brief Channels number in a conversion group. + */ +typedef uint16_t adc_channels_num_t; + +/** + * @brief Possible ADC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + ADC_ERR_OVERFLOW = 1, /**< ADC overflow condition. */ + ADC_ERR_AWD = 2 /**< Analog watchdog triggered. */ +} adcerror_t; + +/** + * @brief Type of a structure representing an ADC driver. + */ +typedef struct ADCDriver ADCDriver; + +/** + * @brief ADC notification callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] buffer pointer to the most recent samples data + * @param[in] n number of buffer rows available starting from @p buffer + */ +typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); + +/** + * @brief ADC error callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + * @note The use of this configuration structure requires knowledge of + * STM32 ADC cell registers interface, please refer to the STM32 + * reference manual for details. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool circular; + /** + * @brief Number of the analog channels belonging to the conversion group. + */ + adc_channels_num_t num_channels; + /** + * @brief Callback function associated to the group or @p NULL. + */ + adccallback_t end_cb; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief ADC CFGR1 register initialization data. + * @note The bits DMAEN and DMACFG are enforced internally + * to the driver, keep them to zero. + * @note The bits @p ADC_CFGR1_CONT or @p ADC_CFGR1_DISCEN must be + * specified in continuous more or if the buffer depth is + * greater than one. + */ + uint32_t cfgr1; +#if (STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE) || defined(__DOXYGEN__) + /** + * @brief ADC CFGR2 register initialization data. + * @note CKMODE bits must not be specified in this field and left to + * zero. + */ + uint32_t cfgr2; +#endif + /** + * @brief ADC TR register initialization data. + */ + uint32_t tr; + /** + * @brief ADC SMPR register initialization data. + */ + uint32_t smpr; + /** + * @brief ADC CHSELR register initialization data. + * @details The number of bits at logic level one in this register must + * be equal to the number in the @p num_channels field. + */ + uint32_t chselr; +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + uint32_t dummy; +} ADCConfig; + +/** + * @brief Structure representing an ADC driver. + */ +struct ADCDriver { + /** + * @brief Driver state. + */ + adcstate_t state; + /** + * @brief Current configuration data. + */ + const ADCConfig *config; + /** + * @brief Current samples buffer pointer or @p NULL. + */ + adcsample_t *samples; + /** + * @brief Current samples buffer depth or @p 0. + */ + size_t depth; + /** + * @brief Current conversion group pointer or @p NULL. + */ + const ADCConversionGroup *grpp; +#if ADC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* ADC_USE_MUTUAL_EXCLUSION */ +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the ADCx registers block. + */ + ADC_TypeDef *adc; + /** + * @brief Pointer to associated DMA channel. + */ + const stm32_dma_stream_t *dmastp; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Changes the value of the ADC CCR register. + * @details Use this function in order to enable or disable the internal + * analog sources. See the documentation in the STM32 Reference + * Manual. + * @note PRESC bits must not be specified and left to zero. + */ +#define adcSTM32SetCCR(ccr) (ADC->CCR = (ccr)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void adc_lld_init(void); + void adc_lld_start(ADCDriver *adcp); + void adc_lld_stop(ADCDriver *adcp); + void adc_lld_start_conversion(ADCDriver *adcp); + void adc_lld_stop_conversion(ADCDriver *adcp); + void adc_lld_serve_interrupt(ADCDriver *adcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ADC */ + +#endif /* _ADC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv2/adc_lld.c b/os/hal/ports/STM32/LLD/ADCv2/adc_lld.c deleted file mode 100644 index 2ff6f6966..000000000 --- a/os/hal/ports/STM32/LLD/ADCv2/adc_lld.c +++ /dev/null @@ -1,428 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/LLD/ADCv2/adc_lld.c - * @brief STM32 ADC subsystem low level driver source. - * - * @addtogroup ADC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define ADC1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_CHN) - -#define ADC2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_CHN) - -#define ADC3_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief ADC1 driver identifier.*/ -#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) -ADCDriver ADCD1; -#endif - -/** @brief ADC2 driver identifier.*/ -#if STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) -ADCDriver ADCD2; -#endif - -/** @brief ADC3 driver identifier.*/ -#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) -ADCDriver ADCD3; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief ADC DMA ISR service routine. - * - * @param[in] adcp pointer to the @p ADCDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) { - - /* DMA errors handling.*/ - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - /* DMA, this could help only if the DMA tries to access an unmapped - address space or violates alignment rules.*/ - _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); - } - else { - /* It is possible that the conversion group has already be reset by the - ADC error handler, in this case this interrupt is spurious.*/ - if (adcp->grpp != NULL) { - - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _adc_isr_full_code(adcp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _adc_isr_half_code(adcp); - } - } - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 || STM32_ADC_USE_ADC3 || \ - defined(__DOXYGEN__) -/** - * @brief ADC interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_ADC_HANDLER) { - uint32_t sr; - - OSAL_IRQ_PROLOGUE(); - -#if STM32_ADC_USE_ADC1 - sr = ADC1->SR; - ADC1->SR = 0; - /* Note, an overflow may occur after the conversion ended before the driver - is able to stop the ADC, this is why the DMA channel is checked too.*/ - if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD1.dmastp) > 0)) { - /* ADC overflow condition, this could happen only if the DMA is unable - to read data fast enough.*/ - if (ADCD1.grpp != NULL) - _adc_isr_error_code(&ADCD1, ADC_ERR_OVERFLOW); - } - /* TODO: Add here analog watchdog handling.*/ -#endif /* STM32_ADC_USE_ADC1 */ - -#if STM32_ADC_USE_ADC2 - sr = ADC2->SR; - ADC2->SR = 0; - /* Note, an overflow may occur after the conversion ended before the driver - is able to stop the ADC, this is why the DMA channel is checked too.*/ - if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD2.dmastp) > 0)) { - /* ADC overflow condition, this could happen only if the DMA is unable - to read data fast enough.*/ - if (ADCD2.grpp != NULL) - _adc_isr_error_code(&ADCD2, ADC_ERR_OVERFLOW); - } - /* TODO: Add here analog watchdog handling.*/ -#endif /* STM32_ADC_USE_ADC2 */ - -#if STM32_ADC_USE_ADC3 - sr = ADC3->SR; - ADC3->SR = 0; - /* Note, an overflow may occur after the conversion ended before the driver - is able to stop the ADC, this is why the DMA channel is checked too.*/ - if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD3.dmastp) > 0)) { - /* ADC overflow condition, this could happen only if the DMA is unable - to read data fast enough.*/ - if (ADCD3.grpp != NULL) - _adc_isr_error_code(&ADCD3, ADC_ERR_OVERFLOW); - } - /* TODO: Add here analog watchdog handling.*/ -#endif /* STM32_ADC_USE_ADC3 */ - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ADC driver initialization. - * - * @notapi - */ -void adc_lld_init(void) { - -#if STM32_ADC_USE_ADC1 - /* Driver initialization.*/ - adcObjectInit(&ADCD1); - ADCD1.adc = ADC1; - ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); - ADCD1.dmamode = STM32_DMA_CR_CHSEL(ADC1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif - -#if STM32_ADC_USE_ADC2 - /* Driver initialization.*/ - adcObjectInit(&ADCD2); - ADCD2.adc = ADC2; - ADCD2.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC2_DMA_STREAM); - ADCD2.dmamode = STM32_DMA_CR_CHSEL(ADC2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_ADC_ADC2_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif - -#if STM32_ADC_USE_ADC3 - /* Driver initialization.*/ - adcObjectInit(&ADCD3); - ADCD3.adc = ADC3; - ADCD3.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC3_DMA_STREAM); - ADCD3.dmamode = STM32_DMA_CR_CHSEL(ADC3_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_ADC_ADC3_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif - - /* The shared vector is initialized on driver initialization and never - disabled because sharing.*/ - nvicEnableVector(STM32_ADC_NUMBER, STM32_ADC_IRQ_PRIORITY); -} - -/** - * @brief Configures and activates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start(ADCDriver *adcp) { - - /* If in stopped state then enables the ADC and DMA clocks.*/ - if (adcp->state == ADC_STOP) { -#if STM32_ADC_USE_ADC1 - if (&ADCD1 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC1_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR); - rccEnableADC1(FALSE); - } -#endif /* STM32_ADC_USE_ADC1 */ - -#if STM32_ADC_USE_ADC2 - if (&ADCD2 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC2_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - dmaStreamSetPeripheral(adcp->dmastp, &ADC2->DR); - rccEnableADC2(FALSE); - } -#endif /* STM32_ADC_USE_ADC2 */ - -#if STM32_ADC_USE_ADC3 - if (&ADCD3 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC3_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - dmaStreamSetPeripheral(adcp->dmastp, &ADC3->DR); - rccEnableADC3(FALSE); - } -#endif /* STM32_ADC_USE_ADC3 */ - - /* This is a common register but apparently it requires that at least one - of the ADCs is clocked in order to allow writing, see bug 3575297.*/ - ADC->CCR = (ADC->CCR & (ADC_CCR_TSVREFE | ADC_CCR_VBATE)) | - (STM32_ADC_ADCPRE << 16); - - /* ADC initial setup, starting the analog part here in order to reduce - the latency when starting a conversion.*/ - adcp->adc->CR1 = 0; - adcp->adc->CR2 = 0; - adcp->adc->CR2 = ADC_CR2_ADON; - } -} - -/** - * @brief Deactivates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop(ADCDriver *adcp) { - - /* If in ready state then disables the ADC clock.*/ - if (adcp->state == ADC_READY) { - dmaStreamRelease(adcp->dmastp); - adcp->adc->CR1 = 0; - adcp->adc->CR2 = 0; - -#if STM32_ADC_USE_ADC1 - if (&ADCD1 == adcp) - rccDisableADC1(FALSE); -#endif - -#if STM32_ADC_USE_ADC2 - if (&ADCD2 == adcp) - rccDisableADC2(FALSE); -#endif - -#if STM32_ADC_USE_ADC3 - if (&ADCD3 == adcp) - rccDisableADC3(FALSE); -#endif - } -} - -/** - * @brief Starts an ADC conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start_conversion(ADCDriver *adcp) { - uint32_t mode; - uint32_t cr2; - const ADCConversionGroup *grpp = adcp->grpp; - - /* DMA setup.*/ - mode = adcp->dmamode; - if (grpp->circular) { - mode |= STM32_DMA_CR_CIRC; - if (adcp->depth > 1) { - /* If circular buffer depth > 1, then the half transfer interrupt - is enabled in order to allow streaming processing.*/ - mode |= STM32_DMA_CR_HTIE; - } - } - dmaStreamSetMemory0(adcp->dmastp, adcp->samples); - dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * - (uint32_t)adcp->depth); - dmaStreamSetMode(adcp->dmastp, mode); - dmaStreamEnable(adcp->dmastp); - - /* ADC setup.*/ - adcp->adc->SR = 0; - adcp->adc->SMPR1 = grpp->smpr1; - adcp->adc->SMPR2 = grpp->smpr2; - adcp->adc->SQR1 = grpp->sqr1; - adcp->adc->SQR2 = grpp->sqr2; - adcp->adc->SQR3 = grpp->sqr3; - - /* ADC configuration and start.*/ - adcp->adc->CR1 = grpp->cr1 | ADC_CR1_OVRIE | ADC_CR1_SCAN; - - /* Enforcing the mandatory bits in CR2.*/ - cr2 = grpp->cr2 | ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_ADON; - - /* The start method is different dependign if HW or SW triggered, the - start is performed using the method specified in the CR2 configuration.*/ - if ((cr2 & ADC_CR2_SWSTART) != 0) { - /* Initializing CR2 while keeping ADC_CR2_SWSTART at zero.*/ - adcp->adc->CR2 = (cr2 | ADC_CR2_CONT) & ~ADC_CR2_SWSTART; - - /* Finally enabling ADC_CR2_SWSTART.*/ - adcp->adc->CR2 = (cr2 | ADC_CR2_CONT); - } - else - adcp->adc->CR2 = cr2; -} - -/** - * @brief Stops an ongoing conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop_conversion(ADCDriver *adcp) { - - dmaStreamDisable(adcp->dmastp); - adcp->adc->CR1 = 0; - adcp->adc->CR2 = 0; - adcp->adc->CR2 = ADC_CR2_ADON; -} - -/** - * @brief Enables the TSVREFE bit. - * @details The TSVREFE bit is required in order to sample the internal - * temperature sensor and internal reference voltage. - * @note This is an STM32-only functionality. - */ -void adcSTM32EnableTSVREFE(void) { - - ADC->CCR |= ADC_CCR_TSVREFE; -} - -/** - * @brief Disables the TSVREFE bit. - * @details The TSVREFE bit is required in order to sample the internal - * temperature sensor and internal reference voltage. - * @note This is an STM32-only functionality. - */ -void adcSTM32DisableTSVREFE(void) { - - ADC->CCR &= ~ADC_CCR_TSVREFE; -} - -/** - * @brief Enables the VBATE bit. - * @details The VBATE bit is required in order to sample the VBAT channel. - * @note This is an STM32-only functionality. - * @note This function is meant to be called after @p adcStart(). - */ -void adcSTM32EnableVBATE(void) { - - ADC->CCR |= ADC_CCR_VBATE; -} - -/** - * @brief Disables the VBATE bit. - * @details The VBATE bit is required in order to sample the VBAT channel. - * @note This is an STM32-only functionality. - * @note This function is meant to be called after @p adcStart(). - */ -void adcSTM32DisableVBATE(void) { - - ADC->CCR &= ~ADC_CCR_VBATE; -} - -#endif /* HAL_USE_ADC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv2/adc_lld.h b/os/hal/ports/STM32/LLD/ADCv2/adc_lld.h deleted file mode 100644 index 2e04f73d8..000000000 --- a/os/hal/ports/STM32/LLD/ADCv2/adc_lld.h +++ /dev/null @@ -1,570 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/LLD/ADCv2/adc_lld.h - * @brief STM32 ADC subsystem low level driver header. - * - * @addtogroup ADC - * @{ - */ - -#ifndef _ADC_LLD_H_ -#define _ADC_LLD_H_ - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Absolute Maximum Ratings - * @{ - */ -/** - * @brief Minimum ADC clock frequency. - */ -#define STM32_ADCCLK_MIN 600000 - -/** - * @brief Maximum ADC clock frequency. - */ -#if defined(STM32F4XX) || defined(__DOXYGEN__) -#define STM32_ADCCLK_MAX 36000000 -#else -#define STM32_ADCCLK_MAX 30000000 -#endif -/** @} */ - -/** - * @name Triggers selection - * @{ - */ -#define ADC_CR2_EXTEN_MASK (3U << 28U) -#define ADC_CR2_EXTEN_DISABLED (0U << 28U) -#define ADC_CR2_EXTEN_RISING (1U << 28U) -#define ADC_CR2_EXTEN_FALLING (2U << 28U) -#define ADC_CR2_EXTEN_BOTH (3U << 28U) - -#define ADC_CR2_EXTSEL_MASK (15U << 24U) -#define ADC_CR2_EXTSEL_SRC(n) ((n) << 24U) -/** @} */ - -/** - * @name ADC clock divider settings - * @{ - */ -#define ADC_CCR_ADCPRE_DIV2 0 -#define ADC_CCR_ADCPRE_DIV4 1 -#define ADC_CCR_ADCPRE_DIV6 2 -#define ADC_CCR_ADCPRE_DIV8 3 -/** @} */ - -/** - * @name Available analog channels - * @{ - */ -#define ADC_CHANNEL_IN0 0 /**< @brief External analog input 0. */ -#define ADC_CHANNEL_IN1 1 /**< @brief External analog input 1. */ -#define ADC_CHANNEL_IN2 2 /**< @brief External analog input 2. */ -#define ADC_CHANNEL_IN3 3 /**< @brief External analog input 3. */ -#define ADC_CHANNEL_IN4 4 /**< @brief External analog input 4. */ -#define ADC_CHANNEL_IN5 5 /**< @brief External analog input 5. */ -#define ADC_CHANNEL_IN6 6 /**< @brief External analog input 6. */ -#define ADC_CHANNEL_IN7 7 /**< @brief External analog input 7. */ -#define ADC_CHANNEL_IN8 8 /**< @brief External analog input 8. */ -#define ADC_CHANNEL_IN9 9 /**< @brief External analog input 9. */ -#define ADC_CHANNEL_IN10 10 /**< @brief External analog input 10. */ -#define ADC_CHANNEL_IN11 11 /**< @brief External analog input 11. */ -#define ADC_CHANNEL_IN12 12 /**< @brief External analog input 12. */ -#define ADC_CHANNEL_IN13 13 /**< @brief External analog input 13. */ -#define ADC_CHANNEL_IN14 14 /**< @brief External analog input 14. */ -#define ADC_CHANNEL_IN15 15 /**< @brief External analog input 15. */ -#define ADC_CHANNEL_SENSOR 16 /**< @brief Internal temperature sensor. - @note Available onADC1 only. */ -#define ADC_CHANNEL_VREFINT 17 /**< @brief Internal reference. - @note Available onADC1 only. */ -#define ADC_CHANNEL_VBAT 18 /**< @brief VBAT. - @note Available onADC1 only. */ -/** @} */ - -/** - * @name Sampling rates - * @{ - */ -#define ADC_SAMPLE_3 0 /**< @brief 3 cycles sampling time. */ -#define ADC_SAMPLE_15 1 /**< @brief 15 cycles sampling time. */ -#define ADC_SAMPLE_28 2 /**< @brief 28 cycles sampling time. */ -#define ADC_SAMPLE_56 3 /**< @brief 56 cycles sampling time. */ -#define ADC_SAMPLE_84 4 /**< @brief 84 cycles sampling time. */ -#define ADC_SAMPLE_112 5 /**< @brief 112 cycles sampling time. */ -#define ADC_SAMPLE_144 6 /**< @brief 144 cycles sampling time. */ -#define ADC_SAMPLE_480 7 /**< @brief 480 cycles sampling time. */ -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief ADC common clock divider. - * @note This setting is influenced by the VDDA voltage and other - * external conditions, please refer to the datasheet for more - * info.
- * See section 5.3.20 "12-bit ADC characteristics". - */ -#if !defined(STM32_ADC_ADCPRE) || defined(__DOXYGEN__) -#define STM32_ADC_ADCPRE ADC_CCR_ADCPRE_DIV2 -#endif - -/** - * @brief ADC1 driver enable switch. - * @details If set to @p TRUE the support for ADC1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC1 FALSE -#endif - -/** - * @brief ADC2 driver enable switch. - * @details If set to @p TRUE the support for ADC2 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ADC_USE_ADC2) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC2 FALSE -#endif - -/** - * @brief ADC3 driver enable switch. - * @details If set to @p TRUE the support for ADC3 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ADC_USE_ADC3) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC3 FALSE -#endif - -/** - * @brief DMA stream used for ADC1 operations. - */ -#if !defined(STM32_ADC_ADC1_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(2, 4) -#endif - -/** - * @brief DMA stream used for ADC2 operations. - */ -#if !defined(STM32_ADC_ADC2_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_ADC_ADC2_DMA_STREAM STM32_DMA_STREAM_ID(2, 2) -#endif - -/** - * @brief DMA stream used for ADC3 operations. - */ -#if !defined(STM32_ADC_ADC3_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_ADC_ADC3_DMA_STREAM STM32_DMA_STREAM_ID(2, 1) -#endif - -/** - * @brief ADC1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC2_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC3 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC3_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC interrupt priority level setting. - * @note This setting is shared among ADC1, ADC2 and ADC3 because - * all ADCs share the same vector. - */ -#if !defined(STM32_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC1 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC2 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC2_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC3 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY 5 -#endif - -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 && !STM32_HAS_ADC1 -#error "ADC1 not present in the selected device" -#endif - -#if STM32_ADC_USE_ADC2 && !STM32_HAS_ADC2 -#error "ADC2 not present in the selected device" -#endif - -#if STM32_ADC_USE_ADC3 && !STM32_HAS_ADC3 -#error "ADC3 not present in the selected device" -#endif - -#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 -#error "ADC driver activated but no ADC peripheral assigned" -#endif - -#if STM32_ADC_USE_ADC1 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK) -#error "invalid DMA stream associated to ADC1" -#endif - -#if STM32_ADC_USE_ADC2 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_MSK) -#error "invalid DMA stream associated to ADC2" -#endif - -#if STM32_ADC_USE_ADC3 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_MSK) -#error "invalid DMA stream associated to ADC3" -#endif - -/* ADC clock related settings and checks.*/ -#if STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV2 -#define STM32_ADCCLK (STM32_PCLK2 / 2) -#elif STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV4 -#define STM32_ADCCLK (STM32_PCLK2 / 4) -#elif STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV6 -#define STM32_ADCCLK (STM32_PCLK2 / 6) -#elif STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV8 -#define STM32_ADCCLK (STM32_PCLK2 / 8) -#else -#error "invalid STM32_ADC_ADCPRE value specified" -#endif - -#if (STM32_ADCCLK < STM32_ADCCLK_MIN) || (STM32_ADCCLK > STM32_ADCCLK_MAX) -#error "STM32_ADCCLK outside acceptable range (STM32_ADCCLK_MIN...STM32_ADCCLK_MAX)" -#endif - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief ADC sample data type. - */ -typedef uint16_t adcsample_t; - -/** - * @brief Channels number in a conversion group. - */ -typedef uint16_t adc_channels_num_t; - -/** - * @brief Possible ADC failure causes. - * @note Error codes are architecture dependent and should not relied - * upon. - */ -typedef enum { - ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ - ADC_ERR_OVERFLOW = 1 /**< ADC overflow condition. */ -} adcerror_t; - -/** - * @brief Type of a structure representing an ADC driver. - */ -typedef struct ADCDriver ADCDriver; - -/** - * @brief ADC notification callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] buffer pointer to the most recent samples data - * @param[in] n number of buffer rows available starting from @p buffer - */ -typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); - -/** - * @brief ADC error callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] err ADC error code - */ -typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); - -/** - * @brief Conversion group configuration structure. - * @details This implementation-dependent structure describes a conversion - * operation. - * @note The use of this configuration structure requires knowledge of - * STM32 ADC cell registers interface, please refer to the STM32 - * reference manual for details. - */ -typedef struct { - /** - * @brief Enables the circular buffer mode for the group. - */ - bool circular; - /** - * @brief Number of the analog channels belonging to the conversion group. - */ - adc_channels_num_t num_channels; - /** - * @brief Callback function associated to the group or @p NULL. - */ - adccallback_t end_cb; - /** - * @brief Error callback or @p NULL. - */ - adcerrorcallback_t error_cb; - /* End of the mandatory fields.*/ - /** - * @brief ADC CR1 register initialization data. - * @note All the required bits must be defined into this field except - * @p ADC_CR1_SCAN that is enforced inside the driver. - */ - uint32_t cr1; - /** - * @brief ADC CR2 register initialization data. - * @note All the required bits must be defined into this field except - * @p ADC_CR2_DMA, @p ADC_CR2_CONT and @p ADC_CR2_ADON that are - * enforced inside the driver. - */ - uint32_t cr2; - /** - * @brief ADC SMPR1 register initialization data. - * @details In this field must be specified the sample times for channels - * 10...18. - */ - uint32_t smpr1; - /** - * @brief ADC SMPR2 register initialization data. - * @details In this field must be specified the sample times for channels - * 0...9. - */ - uint32_t smpr2; - /** - * @brief ADC SQR1 register initialization data. - * @details Conversion group sequence 13...16 + sequence length. - */ - uint32_t sqr1; - /** - * @brief ADC SQR2 register initialization data. - * @details Conversion group sequence 7...12. - */ - uint32_t sqr2; - /** - * @brief ADC SQR3 register initialization data. - * @details Conversion group sequence 1...6. - */ - uint32_t sqr3; -} ADCConversionGroup; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - uint32_t dummy; -} ADCConfig; - -/** - * @brief Structure representing an ADC driver. - */ -struct ADCDriver { - /** - * @brief Driver state. - */ - adcstate_t state; - /** - * @brief Current configuration data. - */ - const ADCConfig *config; - /** - * @brief Current samples buffer pointer or @p NULL. - */ - adcsample_t *samples; - /** - * @brief Current samples buffer depth or @p 0. - */ - size_t depth; - /** - * @brief Current conversion group pointer or @p NULL. - */ - const ADCConversionGroup *grpp; -#if ADC_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif -#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* ADC_USE_MUTUAL_EXCLUSION */ -#if defined(ADC_DRIVER_EXT_FIELDS) - ADC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the ADCx registers block. - */ - ADC_TypeDef *adc; - /** - * @brief Pointer to associated DMA channel. - */ - const stm32_dma_stream_t *dmastp; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @name Sequences building helper macros - * @{ - */ -/** - * @brief Number of channels in a conversion sequence. - */ -#define ADC_SQR1_NUM_CH(n) (((n) - 1) << 20) - -#define ADC_SQR3_SQ1_N(n) ((n) << 0) /**< @brief 1st channel in seq. */ -#define ADC_SQR3_SQ2_N(n) ((n) << 5) /**< @brief 2nd channel in seq. */ -#define ADC_SQR3_SQ3_N(n) ((n) << 10) /**< @brief 3rd channel in seq. */ -#define ADC_SQR3_SQ4_N(n) ((n) << 15) /**< @brief 4th channel in seq. */ -#define ADC_SQR3_SQ5_N(n) ((n) << 20) /**< @brief 5th channel in seq. */ -#define ADC_SQR3_SQ6_N(n) ((n) << 25) /**< @brief 6th channel in seq. */ - -#define ADC_SQR2_SQ7_N(n) ((n) << 0) /**< @brief 7th channel in seq. */ -#define ADC_SQR2_SQ8_N(n) ((n) << 5) /**< @brief 8th channel in seq. */ -#define ADC_SQR2_SQ9_N(n) ((n) << 10) /**< @brief 9th channel in seq. */ -#define ADC_SQR2_SQ10_N(n) ((n) << 15) /**< @brief 10th channel in seq.*/ -#define ADC_SQR2_SQ11_N(n) ((n) << 20) /**< @brief 11th channel in seq.*/ -#define ADC_SQR2_SQ12_N(n) ((n) << 25) /**< @brief 12th channel in seq.*/ - -#define ADC_SQR1_SQ13_N(n) ((n) << 0) /**< @brief 13th channel in seq.*/ -#define ADC_SQR1_SQ14_N(n) ((n) << 5) /**< @brief 14th channel in seq.*/ -#define ADC_SQR1_SQ15_N(n) ((n) << 10) /**< @brief 15th channel in seq.*/ -#define ADC_SQR1_SQ16_N(n) ((n) << 15) /**< @brief 16th channel in seq.*/ -/** @} */ - -/** - * @name Sampling rate settings helper macros - * @{ - */ -#define ADC_SMPR2_SMP_AN0(n) ((n) << 0) /**< @brief AN0 sampling time. */ -#define ADC_SMPR2_SMP_AN1(n) ((n) << 3) /**< @brief AN1 sampling time. */ -#define ADC_SMPR2_SMP_AN2(n) ((n) << 6) /**< @brief AN2 sampling time. */ -#define ADC_SMPR2_SMP_AN3(n) ((n) << 9) /**< @brief AN3 sampling time. */ -#define ADC_SMPR2_SMP_AN4(n) ((n) << 12) /**< @brief AN4 sampling time. */ -#define ADC_SMPR2_SMP_AN5(n) ((n) << 15) /**< @brief AN5 sampling time. */ -#define ADC_SMPR2_SMP_AN6(n) ((n) << 18) /**< @brief AN6 sampling time. */ -#define ADC_SMPR2_SMP_AN7(n) ((n) << 21) /**< @brief AN7 sampling time. */ -#define ADC_SMPR2_SMP_AN8(n) ((n) << 24) /**< @brief AN8 sampling time. */ -#define ADC_SMPR2_SMP_AN9(n) ((n) << 27) /**< @brief AN9 sampling time. */ - -#define ADC_SMPR1_SMP_AN10(n) ((n) << 0) /**< @brief AN10 sampling time. */ -#define ADC_SMPR1_SMP_AN11(n) ((n) << 3) /**< @brief AN11 sampling time. */ -#define ADC_SMPR1_SMP_AN12(n) ((n) << 6) /**< @brief AN12 sampling time. */ -#define ADC_SMPR1_SMP_AN13(n) ((n) << 9) /**< @brief AN13 sampling time. */ -#define ADC_SMPR1_SMP_AN14(n) ((n) << 12) /**< @brief AN14 sampling time. */ -#define ADC_SMPR1_SMP_AN15(n) ((n) << 15) /**< @brief AN15 sampling time. */ -#define ADC_SMPR1_SMP_SENSOR(n) ((n) << 18) /**< @brief Temperature Sensor - sampling time. */ -#define ADC_SMPR1_SMP_VREF(n) ((n) << 21) /**< @brief Voltage Reference - sampling time. */ -#define ADC_SMPR1_SMP_VBAT(n) ((n) << 24) /**< @brief VBAT sampling time. */ -/** @} */ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 && !defined(__DOXYGEN__) -extern ADCDriver ADCD1; -#endif - -#if STM32_ADC_USE_ADC2 && !defined(__DOXYGEN__) -extern ADCDriver ADCD2; -#endif - -#if STM32_ADC_USE_ADC3 && !defined(__DOXYGEN__) -extern ADCDriver ADCD3; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void adc_lld_init(void); - void adc_lld_start(ADCDriver *adcp); - void adc_lld_stop(ADCDriver *adcp); - void adc_lld_start_conversion(ADCDriver *adcp); - void adc_lld_stop_conversion(ADCDriver *adcp); - void adcSTM32EnableTSVREFE(void); - void adcSTM32DisableTSVREFE(void); - void adcSTM32EnableVBATE(void); - void adcSTM32DisableVBATE(void); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_ADC */ - -#endif /* _ADC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.c b/os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.c new file mode 100644 index 000000000..2ff6f6966 --- /dev/null +++ b/os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.c @@ -0,0 +1,428 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/LLD/ADCv2/adc_lld.c + * @brief STM32 ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define ADC1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_CHN) + +#define ADC2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_CHN) + +#define ADC3_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief ADC1 driver identifier.*/ +#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/** @brief ADC2 driver identifier.*/ +#if STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) +ADCDriver ADCD2; +#endif + +/** @brief ADC3 driver identifier.*/ +#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) +ADCDriver ADCD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief ADC DMA ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void adc_lld_serve_rx_interrupt(ADCDriver *adcp, uint32_t flags) { + + /* DMA errors handling.*/ + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + /* DMA, this could help only if the DMA tries to access an unmapped + address space or violates alignment rules.*/ + _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); + } + else { + /* It is possible that the conversion group has already be reset by the + ADC error handler, in this case this interrupt is spurious.*/ + if (adcp->grpp != NULL) { + + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _adc_isr_full_code(adcp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _adc_isr_half_code(adcp); + } + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 || STM32_ADC_USE_ADC3 || \ + defined(__DOXYGEN__) +/** + * @brief ADC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC_HANDLER) { + uint32_t sr; + + OSAL_IRQ_PROLOGUE(); + +#if STM32_ADC_USE_ADC1 + sr = ADC1->SR; + ADC1->SR = 0; + /* Note, an overflow may occur after the conversion ended before the driver + is able to stop the ADC, this is why the DMA channel is checked too.*/ + if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD1.dmastp) > 0)) { + /* ADC overflow condition, this could happen only if the DMA is unable + to read data fast enough.*/ + if (ADCD1.grpp != NULL) + _adc_isr_error_code(&ADCD1, ADC_ERR_OVERFLOW); + } + /* TODO: Add here analog watchdog handling.*/ +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC2 + sr = ADC2->SR; + ADC2->SR = 0; + /* Note, an overflow may occur after the conversion ended before the driver + is able to stop the ADC, this is why the DMA channel is checked too.*/ + if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD2.dmastp) > 0)) { + /* ADC overflow condition, this could happen only if the DMA is unable + to read data fast enough.*/ + if (ADCD2.grpp != NULL) + _adc_isr_error_code(&ADCD2, ADC_ERR_OVERFLOW); + } + /* TODO: Add here analog watchdog handling.*/ +#endif /* STM32_ADC_USE_ADC2 */ + +#if STM32_ADC_USE_ADC3 + sr = ADC3->SR; + ADC3->SR = 0; + /* Note, an overflow may occur after the conversion ended before the driver + is able to stop the ADC, this is why the DMA channel is checked too.*/ + if ((sr & ADC_SR_OVR) && (dmaStreamGetTransactionSize(ADCD3.dmastp) > 0)) { + /* ADC overflow condition, this could happen only if the DMA is unable + to read data fast enough.*/ + if (ADCD3.grpp != NULL) + _adc_isr_error_code(&ADCD3, ADC_ERR_OVERFLOW); + } + /* TODO: Add here analog watchdog handling.*/ +#endif /* STM32_ADC_USE_ADC3 */ + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + +#if STM32_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); + ADCD1.adc = ADC1; + ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); + ADCD1.dmamode = STM32_DMA_CR_CHSEL(ADC1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif + +#if STM32_ADC_USE_ADC2 + /* Driver initialization.*/ + adcObjectInit(&ADCD2); + ADCD2.adc = ADC2; + ADCD2.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC2_DMA_STREAM); + ADCD2.dmamode = STM32_DMA_CR_CHSEL(ADC2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_ADC_ADC2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif + +#if STM32_ADC_USE_ADC3 + /* Driver initialization.*/ + adcObjectInit(&ADCD3); + ADCD3.adc = ADC3; + ADCD3.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC3_DMA_STREAM); + ADCD3.dmamode = STM32_DMA_CR_CHSEL(ADC3_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_ADC_ADC3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif + + /* The shared vector is initialized on driver initialization and never + disabled because sharing.*/ + nvicEnableVector(STM32_ADC_NUMBER, STM32_ADC_IRQ_PRIORITY); +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* If in stopped state then enables the ADC and DMA clocks.*/ + if (adcp->state == ADC_STOP) { +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC1_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + dmaStreamSetPeripheral(adcp->dmastp, &ADC1->DR); + rccEnableADC1(FALSE); + } +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC2 + if (&ADCD2 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC2_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + dmaStreamSetPeripheral(adcp->dmastp, &ADC2->DR); + rccEnableADC2(FALSE); + } +#endif /* STM32_ADC_USE_ADC2 */ + +#if STM32_ADC_USE_ADC3 + if (&ADCD3 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC3_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_rx_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + dmaStreamSetPeripheral(adcp->dmastp, &ADC3->DR); + rccEnableADC3(FALSE); + } +#endif /* STM32_ADC_USE_ADC3 */ + + /* This is a common register but apparently it requires that at least one + of the ADCs is clocked in order to allow writing, see bug 3575297.*/ + ADC->CCR = (ADC->CCR & (ADC_CCR_TSVREFE | ADC_CCR_VBATE)) | + (STM32_ADC_ADCPRE << 16); + + /* ADC initial setup, starting the analog part here in order to reduce + the latency when starting a conversion.*/ + adcp->adc->CR1 = 0; + adcp->adc->CR2 = 0; + adcp->adc->CR2 = ADC_CR2_ADON; + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock.*/ + if (adcp->state == ADC_READY) { + dmaStreamRelease(adcp->dmastp); + adcp->adc->CR1 = 0; + adcp->adc->CR2 = 0; + +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) + rccDisableADC1(FALSE); +#endif + +#if STM32_ADC_USE_ADC2 + if (&ADCD2 == adcp) + rccDisableADC2(FALSE); +#endif + +#if STM32_ADC_USE_ADC3 + if (&ADCD3 == adcp) + rccDisableADC3(FALSE); +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + uint32_t mode; + uint32_t cr2; + const ADCConversionGroup *grpp = adcp->grpp; + + /* DMA setup.*/ + mode = adcp->dmamode; + if (grpp->circular) { + mode |= STM32_DMA_CR_CIRC; + if (adcp->depth > 1) { + /* If circular buffer depth > 1, then the half transfer interrupt + is enabled in order to allow streaming processing.*/ + mode |= STM32_DMA_CR_HTIE; + } + } + dmaStreamSetMemory0(adcp->dmastp, adcp->samples); + dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * + (uint32_t)adcp->depth); + dmaStreamSetMode(adcp->dmastp, mode); + dmaStreamEnable(adcp->dmastp); + + /* ADC setup.*/ + adcp->adc->SR = 0; + adcp->adc->SMPR1 = grpp->smpr1; + adcp->adc->SMPR2 = grpp->smpr2; + adcp->adc->SQR1 = grpp->sqr1; + adcp->adc->SQR2 = grpp->sqr2; + adcp->adc->SQR3 = grpp->sqr3; + + /* ADC configuration and start.*/ + adcp->adc->CR1 = grpp->cr1 | ADC_CR1_OVRIE | ADC_CR1_SCAN; + + /* Enforcing the mandatory bits in CR2.*/ + cr2 = grpp->cr2 | ADC_CR2_DMA | ADC_CR2_DDS | ADC_CR2_ADON; + + /* The start method is different dependign if HW or SW triggered, the + start is performed using the method specified in the CR2 configuration.*/ + if ((cr2 & ADC_CR2_SWSTART) != 0) { + /* Initializing CR2 while keeping ADC_CR2_SWSTART at zero.*/ + adcp->adc->CR2 = (cr2 | ADC_CR2_CONT) & ~ADC_CR2_SWSTART; + + /* Finally enabling ADC_CR2_SWSTART.*/ + adcp->adc->CR2 = (cr2 | ADC_CR2_CONT); + } + else + adcp->adc->CR2 = cr2; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + + dmaStreamDisable(adcp->dmastp); + adcp->adc->CR1 = 0; + adcp->adc->CR2 = 0; + adcp->adc->CR2 = ADC_CR2_ADON; +} + +/** + * @brief Enables the TSVREFE bit. + * @details The TSVREFE bit is required in order to sample the internal + * temperature sensor and internal reference voltage. + * @note This is an STM32-only functionality. + */ +void adcSTM32EnableTSVREFE(void) { + + ADC->CCR |= ADC_CCR_TSVREFE; +} + +/** + * @brief Disables the TSVREFE bit. + * @details The TSVREFE bit is required in order to sample the internal + * temperature sensor and internal reference voltage. + * @note This is an STM32-only functionality. + */ +void adcSTM32DisableTSVREFE(void) { + + ADC->CCR &= ~ADC_CCR_TSVREFE; +} + +/** + * @brief Enables the VBATE bit. + * @details The VBATE bit is required in order to sample the VBAT channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + */ +void adcSTM32EnableVBATE(void) { + + ADC->CCR |= ADC_CCR_VBATE; +} + +/** + * @brief Disables the VBATE bit. + * @details The VBATE bit is required in order to sample the VBAT channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + */ +void adcSTM32DisableVBATE(void) { + + ADC->CCR &= ~ADC_CCR_VBATE; +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.h b/os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.h new file mode 100644 index 000000000..2e04f73d8 --- /dev/null +++ b/os/hal/ports/STM32/LLD/ADCv2/hal_adc_lld.h @@ -0,0 +1,570 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/LLD/ADCv2/adc_lld.h + * @brief STM32 ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef _ADC_LLD_H_ +#define _ADC_LLD_H_ + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Absolute Maximum Ratings + * @{ + */ +/** + * @brief Minimum ADC clock frequency. + */ +#define STM32_ADCCLK_MIN 600000 + +/** + * @brief Maximum ADC clock frequency. + */ +#if defined(STM32F4XX) || defined(__DOXYGEN__) +#define STM32_ADCCLK_MAX 36000000 +#else +#define STM32_ADCCLK_MAX 30000000 +#endif +/** @} */ + +/** + * @name Triggers selection + * @{ + */ +#define ADC_CR2_EXTEN_MASK (3U << 28U) +#define ADC_CR2_EXTEN_DISABLED (0U << 28U) +#define ADC_CR2_EXTEN_RISING (1U << 28U) +#define ADC_CR2_EXTEN_FALLING (2U << 28U) +#define ADC_CR2_EXTEN_BOTH (3U << 28U) + +#define ADC_CR2_EXTSEL_MASK (15U << 24U) +#define ADC_CR2_EXTSEL_SRC(n) ((n) << 24U) +/** @} */ + +/** + * @name ADC clock divider settings + * @{ + */ +#define ADC_CCR_ADCPRE_DIV2 0 +#define ADC_CCR_ADCPRE_DIV4 1 +#define ADC_CCR_ADCPRE_DIV6 2 +#define ADC_CCR_ADCPRE_DIV8 3 +/** @} */ + +/** + * @name Available analog channels + * @{ + */ +#define ADC_CHANNEL_IN0 0 /**< @brief External analog input 0. */ +#define ADC_CHANNEL_IN1 1 /**< @brief External analog input 1. */ +#define ADC_CHANNEL_IN2 2 /**< @brief External analog input 2. */ +#define ADC_CHANNEL_IN3 3 /**< @brief External analog input 3. */ +#define ADC_CHANNEL_IN4 4 /**< @brief External analog input 4. */ +#define ADC_CHANNEL_IN5 5 /**< @brief External analog input 5. */ +#define ADC_CHANNEL_IN6 6 /**< @brief External analog input 6. */ +#define ADC_CHANNEL_IN7 7 /**< @brief External analog input 7. */ +#define ADC_CHANNEL_IN8 8 /**< @brief External analog input 8. */ +#define ADC_CHANNEL_IN9 9 /**< @brief External analog input 9. */ +#define ADC_CHANNEL_IN10 10 /**< @brief External analog input 10. */ +#define ADC_CHANNEL_IN11 11 /**< @brief External analog input 11. */ +#define ADC_CHANNEL_IN12 12 /**< @brief External analog input 12. */ +#define ADC_CHANNEL_IN13 13 /**< @brief External analog input 13. */ +#define ADC_CHANNEL_IN14 14 /**< @brief External analog input 14. */ +#define ADC_CHANNEL_IN15 15 /**< @brief External analog input 15. */ +#define ADC_CHANNEL_SENSOR 16 /**< @brief Internal temperature sensor. + @note Available onADC1 only. */ +#define ADC_CHANNEL_VREFINT 17 /**< @brief Internal reference. + @note Available onADC1 only. */ +#define ADC_CHANNEL_VBAT 18 /**< @brief VBAT. + @note Available onADC1 only. */ +/** @} */ + +/** + * @name Sampling rates + * @{ + */ +#define ADC_SAMPLE_3 0 /**< @brief 3 cycles sampling time. */ +#define ADC_SAMPLE_15 1 /**< @brief 15 cycles sampling time. */ +#define ADC_SAMPLE_28 2 /**< @brief 28 cycles sampling time. */ +#define ADC_SAMPLE_56 3 /**< @brief 56 cycles sampling time. */ +#define ADC_SAMPLE_84 4 /**< @brief 84 cycles sampling time. */ +#define ADC_SAMPLE_112 5 /**< @brief 112 cycles sampling time. */ +#define ADC_SAMPLE_144 6 /**< @brief 144 cycles sampling time. */ +#define ADC_SAMPLE_480 7 /**< @brief 480 cycles sampling time. */ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief ADC common clock divider. + * @note This setting is influenced by the VDDA voltage and other + * external conditions, please refer to the datasheet for more + * info.
+ * See section 5.3.20 "12-bit ADC characteristics". + */ +#if !defined(STM32_ADC_ADCPRE) || defined(__DOXYGEN__) +#define STM32_ADC_ADCPRE ADC_CCR_ADCPRE_DIV2 +#endif + +/** + * @brief ADC1 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC1 FALSE +#endif + +/** + * @brief ADC2 driver enable switch. + * @details If set to @p TRUE the support for ADC2 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ADC_USE_ADC2) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC2 FALSE +#endif + +/** + * @brief ADC3 driver enable switch. + * @details If set to @p TRUE the support for ADC3 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ADC_USE_ADC3) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC3 FALSE +#endif + +/** + * @brief DMA stream used for ADC1 operations. + */ +#if !defined(STM32_ADC_ADC1_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(2, 4) +#endif + +/** + * @brief DMA stream used for ADC2 operations. + */ +#if !defined(STM32_ADC_ADC2_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_ADC_ADC2_DMA_STREAM STM32_DMA_STREAM_ID(2, 2) +#endif + +/** + * @brief DMA stream used for ADC3 operations. + */ +#if !defined(STM32_ADC_ADC3_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_ADC_ADC3_DMA_STREAM STM32_DMA_STREAM_ID(2, 1) +#endif + +/** + * @brief ADC1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC2_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC3 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC3_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC interrupt priority level setting. + * @note This setting is shared among ADC1, ADC2 and ADC3 because + * all ADCs share the same vector. + */ +#if !defined(STM32_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC1 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC2 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC2_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC3 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY 5 +#endif + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 && !STM32_HAS_ADC1 +#error "ADC1 not present in the selected device" +#endif + +#if STM32_ADC_USE_ADC2 && !STM32_HAS_ADC2 +#error "ADC2 not present in the selected device" +#endif + +#if STM32_ADC_USE_ADC3 && !STM32_HAS_ADC3 +#error "ADC3 not present in the selected device" +#endif + +#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +#if STM32_ADC_USE_ADC1 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK) +#error "invalid DMA stream associated to ADC1" +#endif + +#if STM32_ADC_USE_ADC2 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_MSK) +#error "invalid DMA stream associated to ADC2" +#endif + +#if STM32_ADC_USE_ADC3 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_MSK) +#error "invalid DMA stream associated to ADC3" +#endif + +/* ADC clock related settings and checks.*/ +#if STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV2 +#define STM32_ADCCLK (STM32_PCLK2 / 2) +#elif STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV4 +#define STM32_ADCCLK (STM32_PCLK2 / 4) +#elif STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV6 +#define STM32_ADCCLK (STM32_PCLK2 / 6) +#elif STM32_ADC_ADCPRE == ADC_CCR_ADCPRE_DIV8 +#define STM32_ADCCLK (STM32_PCLK2 / 8) +#else +#error "invalid STM32_ADC_ADCPRE value specified" +#endif + +#if (STM32_ADCCLK < STM32_ADCCLK_MIN) || (STM32_ADCCLK > STM32_ADCCLK_MAX) +#error "STM32_ADCCLK outside acceptable range (STM32_ADCCLK_MIN...STM32_ADCCLK_MAX)" +#endif + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +typedef uint16_t adcsample_t; + +/** + * @brief Channels number in a conversion group. + */ +typedef uint16_t adc_channels_num_t; + +/** + * @brief Possible ADC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + ADC_ERR_OVERFLOW = 1 /**< ADC overflow condition. */ +} adcerror_t; + +/** + * @brief Type of a structure representing an ADC driver. + */ +typedef struct ADCDriver ADCDriver; + +/** + * @brief ADC notification callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] buffer pointer to the most recent samples data + * @param[in] n number of buffer rows available starting from @p buffer + */ +typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); + +/** + * @brief ADC error callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + * @note The use of this configuration structure requires knowledge of + * STM32 ADC cell registers interface, please refer to the STM32 + * reference manual for details. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool circular; + /** + * @brief Number of the analog channels belonging to the conversion group. + */ + adc_channels_num_t num_channels; + /** + * @brief Callback function associated to the group or @p NULL. + */ + adccallback_t end_cb; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief ADC CR1 register initialization data. + * @note All the required bits must be defined into this field except + * @p ADC_CR1_SCAN that is enforced inside the driver. + */ + uint32_t cr1; + /** + * @brief ADC CR2 register initialization data. + * @note All the required bits must be defined into this field except + * @p ADC_CR2_DMA, @p ADC_CR2_CONT and @p ADC_CR2_ADON that are + * enforced inside the driver. + */ + uint32_t cr2; + /** + * @brief ADC SMPR1 register initialization data. + * @details In this field must be specified the sample times for channels + * 10...18. + */ + uint32_t smpr1; + /** + * @brief ADC SMPR2 register initialization data. + * @details In this field must be specified the sample times for channels + * 0...9. + */ + uint32_t smpr2; + /** + * @brief ADC SQR1 register initialization data. + * @details Conversion group sequence 13...16 + sequence length. + */ + uint32_t sqr1; + /** + * @brief ADC SQR2 register initialization data. + * @details Conversion group sequence 7...12. + */ + uint32_t sqr2; + /** + * @brief ADC SQR3 register initialization data. + * @details Conversion group sequence 1...6. + */ + uint32_t sqr3; +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + uint32_t dummy; +} ADCConfig; + +/** + * @brief Structure representing an ADC driver. + */ +struct ADCDriver { + /** + * @brief Driver state. + */ + adcstate_t state; + /** + * @brief Current configuration data. + */ + const ADCConfig *config; + /** + * @brief Current samples buffer pointer or @p NULL. + */ + adcsample_t *samples; + /** + * @brief Current samples buffer depth or @p 0. + */ + size_t depth; + /** + * @brief Current conversion group pointer or @p NULL. + */ + const ADCConversionGroup *grpp; +#if ADC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* ADC_USE_MUTUAL_EXCLUSION */ +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the ADCx registers block. + */ + ADC_TypeDef *adc; + /** + * @brief Pointer to associated DMA channel. + */ + const stm32_dma_stream_t *dmastp; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @name Sequences building helper macros + * @{ + */ +/** + * @brief Number of channels in a conversion sequence. + */ +#define ADC_SQR1_NUM_CH(n) (((n) - 1) << 20) + +#define ADC_SQR3_SQ1_N(n) ((n) << 0) /**< @brief 1st channel in seq. */ +#define ADC_SQR3_SQ2_N(n) ((n) << 5) /**< @brief 2nd channel in seq. */ +#define ADC_SQR3_SQ3_N(n) ((n) << 10) /**< @brief 3rd channel in seq. */ +#define ADC_SQR3_SQ4_N(n) ((n) << 15) /**< @brief 4th channel in seq. */ +#define ADC_SQR3_SQ5_N(n) ((n) << 20) /**< @brief 5th channel in seq. */ +#define ADC_SQR3_SQ6_N(n) ((n) << 25) /**< @brief 6th channel in seq. */ + +#define ADC_SQR2_SQ7_N(n) ((n) << 0) /**< @brief 7th channel in seq. */ +#define ADC_SQR2_SQ8_N(n) ((n) << 5) /**< @brief 8th channel in seq. */ +#define ADC_SQR2_SQ9_N(n) ((n) << 10) /**< @brief 9th channel in seq. */ +#define ADC_SQR2_SQ10_N(n) ((n) << 15) /**< @brief 10th channel in seq.*/ +#define ADC_SQR2_SQ11_N(n) ((n) << 20) /**< @brief 11th channel in seq.*/ +#define ADC_SQR2_SQ12_N(n) ((n) << 25) /**< @brief 12th channel in seq.*/ + +#define ADC_SQR1_SQ13_N(n) ((n) << 0) /**< @brief 13th channel in seq.*/ +#define ADC_SQR1_SQ14_N(n) ((n) << 5) /**< @brief 14th channel in seq.*/ +#define ADC_SQR1_SQ15_N(n) ((n) << 10) /**< @brief 15th channel in seq.*/ +#define ADC_SQR1_SQ16_N(n) ((n) << 15) /**< @brief 16th channel in seq.*/ +/** @} */ + +/** + * @name Sampling rate settings helper macros + * @{ + */ +#define ADC_SMPR2_SMP_AN0(n) ((n) << 0) /**< @brief AN0 sampling time. */ +#define ADC_SMPR2_SMP_AN1(n) ((n) << 3) /**< @brief AN1 sampling time. */ +#define ADC_SMPR2_SMP_AN2(n) ((n) << 6) /**< @brief AN2 sampling time. */ +#define ADC_SMPR2_SMP_AN3(n) ((n) << 9) /**< @brief AN3 sampling time. */ +#define ADC_SMPR2_SMP_AN4(n) ((n) << 12) /**< @brief AN4 sampling time. */ +#define ADC_SMPR2_SMP_AN5(n) ((n) << 15) /**< @brief AN5 sampling time. */ +#define ADC_SMPR2_SMP_AN6(n) ((n) << 18) /**< @brief AN6 sampling time. */ +#define ADC_SMPR2_SMP_AN7(n) ((n) << 21) /**< @brief AN7 sampling time. */ +#define ADC_SMPR2_SMP_AN8(n) ((n) << 24) /**< @brief AN8 sampling time. */ +#define ADC_SMPR2_SMP_AN9(n) ((n) << 27) /**< @brief AN9 sampling time. */ + +#define ADC_SMPR1_SMP_AN10(n) ((n) << 0) /**< @brief AN10 sampling time. */ +#define ADC_SMPR1_SMP_AN11(n) ((n) << 3) /**< @brief AN11 sampling time. */ +#define ADC_SMPR1_SMP_AN12(n) ((n) << 6) /**< @brief AN12 sampling time. */ +#define ADC_SMPR1_SMP_AN13(n) ((n) << 9) /**< @brief AN13 sampling time. */ +#define ADC_SMPR1_SMP_AN14(n) ((n) << 12) /**< @brief AN14 sampling time. */ +#define ADC_SMPR1_SMP_AN15(n) ((n) << 15) /**< @brief AN15 sampling time. */ +#define ADC_SMPR1_SMP_SENSOR(n) ((n) << 18) /**< @brief Temperature Sensor + sampling time. */ +#define ADC_SMPR1_SMP_VREF(n) ((n) << 21) /**< @brief Voltage Reference + sampling time. */ +#define ADC_SMPR1_SMP_VBAT(n) ((n) << 24) /**< @brief VBAT sampling time. */ +/** @} */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#if STM32_ADC_USE_ADC2 && !defined(__DOXYGEN__) +extern ADCDriver ADCD2; +#endif + +#if STM32_ADC_USE_ADC3 && !defined(__DOXYGEN__) +extern ADCDriver ADCD3; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void adc_lld_init(void); + void adc_lld_start(ADCDriver *adcp); + void adc_lld_stop(ADCDriver *adcp); + void adc_lld_start_conversion(ADCDriver *adcp); + void adc_lld_stop_conversion(ADCDriver *adcp); + void adcSTM32EnableTSVREFE(void); + void adcSTM32DisableTSVREFE(void); + void adcSTM32EnableVBATE(void); + void adcSTM32DisableVBATE(void); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ADC */ + +#endif /* _ADC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv3/adc_lld.c b/os/hal/ports/STM32/LLD/ADCv3/adc_lld.c deleted file mode 100644 index 1e56336e0..000000000 --- a/os/hal/ports/STM32/LLD/ADCv3/adc_lld.c +++ /dev/null @@ -1,938 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32F3xx/adc_lld.c - * @brief STM32F3xx ADC subsystem low level driver source. - * - * @addtogroup ADC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define ADC1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_CHN) - -#define ADC2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_CHN) - -#define ADC3_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_CHN) - -#define ADC4_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_ADC_ADC4_DMA_STREAM, STM32_ADC4_DMA_CHN) - -#if STM32_ADC_DUAL_MODE -#if STM32_ADC_COMPACT_SAMPLES -/* Compact type dual mode.*/ -#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD) -#define ADC_DMA_MDMA ADC_CCR_MDMA_HWORD - -#else /* !STM32_ADC_COMPACT_SAMPLES */ -/* Large type dual mode.*/ -#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_PSIZE_WORD) -#define ADC_DMA_MDMA ADC_CCR_MDMA_WORD -#endif /* !STM32_ADC_COMPACT_SAMPLES */ - -#else /* !STM32_ADC_DUAL_MODE */ -#if STM32_ADC_COMPACT_SAMPLES -/* Compact type single mode.*/ -#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_PSIZE_BYTE) -#define ADC_DMA_MDMA ADC_CCR_MDMA_DISABLED - -#else /* !STM32_ADC_COMPACT_SAMPLES */ -/* Large type single mode.*/ -#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD) -#define ADC_DMA_MDMA ADC_CCR_MDMA_DISABLED -#endif /* !STM32_ADC_COMPACT_SAMPLES */ -#endif /* !STM32_ADC_DUAL_MODE */ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief ADC1 driver identifier.*/ -#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) -ADCDriver ADCD1; -#endif - -/** @brief ADC2 driver identifier.*/ -#if STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) -ADCDriver ADCD2; -#endif - -/** @brief ADC3 driver identifier.*/ -#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) -ADCDriver ADCD3; -#endif - -/** @brief ADC4 driver identifier.*/ -#if STM32_ADC_USE_ADC4 || defined(__DOXYGEN__) -ADCDriver ADCD4; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -static const ADCConfig default_config = { - difsel: 0 -}; - -static uint32_t clkmask; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Enables the ADC voltage regulator. - * - * @param[in] adcp pointer to the @p ADCDriver object - */ -static void adc_lld_vreg_on(ADCDriver *adcp) { - -#if defined(STM32F3XX) - adcp->adcm->CR = 0; /* RM 12.4.3.*/ - adcp->adcm->CR = ADC_CR_ADVREGEN_0; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR = ADC_CR_ADVREGEN_0; -#endif - osalSysPolledDelayX(OSAL_US2RTC(STM32_HCLK, 10)); -#endif - -#if defined(STM32L4XX) - adcp->adcm->CR = 0; /* RM 16.3.6.*/ - adcp->adcm->CR = ADC_CR_ADVREGEN; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR = ADC_CR_ADVREGEN; -#endif - osalSysPolledDelayX(OSAL_US2RTC(STM32_HCLK, 20)); -#endif -} - -/** - * @brief Disables the ADC voltage regulator. - * - * @param[in] adcp pointer to the @p ADCDriver object - */ -static void adc_lld_vreg_off(ADCDriver *adcp) { - -#if defined(STM32F3XX) - adcp->adcm->CR = 0; /* RM 12.4.3.*/ - adcp->adcm->CR = ADC_CR_ADVREGEN_1; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR = 0; - adcp->adcs->CR = ADC_CR_ADVREGEN_1; -#endif -#endif - -#if defined(STM32L4XX) - adcp->adcm->CR = 0; /* RM 12.4.3.*/ - adcp->adcm->CR = ADC_CR_DEEPPWD; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR = 0; - adcp->adcs->CR = ADC_CR_DEEPPWD; -#endif -#endif -} - -/** - * @brief Enables the ADC analog circuit. - * - * @param[in] adcp pointer to the @p ADCDriver object - */ -static void adc_lld_analog_on(ADCDriver *adcp) { - -#if defined(STM32F3XX) - adcp->adcm->CR |= ADC_CR_ADEN; - while ((adcp->adcm->ISR & ADC_ISR_ADRD) == 0) - ; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR |= ADC_CR_ADEN; - while ((adcp->adcs->ISR & ADC_ISR_ADRD) == 0) - ; -#endif -#endif - -#if defined(STM32L4XX) - adcp->adcm->CR |= ADC_CR_ADEN; - while ((adcp->adcm->ISR & ADC_ISR_ADRDY) == 0) - ; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR |= ADC_CR_ADEN; - while ((adcp->adcs->ISR & ADC_ISR_ADRDY) == 0) - ; -#endif -#endif -} - -/** - * @brief Disables the ADC analog circuit. - * - * @param[in] adcp pointer to the @p ADCDriver object - */ -static void adc_lld_analog_off(ADCDriver *adcp) { - - adcp->adcm->CR |= ADC_CR_ADDIS; - while ((adcp->adcm->CR & ADC_CR_ADDIS) != 0) - ; -#if STM32_ADC_DUAL_MODE - adcp->adcs->CR |= ADC_CR_ADDIS; - while ((adcp->adcs->CR & ADC_CR_ADDIS) != 0) - ; -#endif -} - -/** - * @brief Calibrates and ADC unit. - * - * @param[in] adcp pointer to the @p ADCDriver object - */ -static void adc_lld_calibrate(ADCDriver *adcp) { - -#if defined(STM32F3XX) - osalDbgAssert(adcp->adcm->CR == ADC_CR_ADVREGEN_0, "invalid register state"); - adcp->adcm->CR |= ADC_CR_ADCAL; - while ((adcp->adcm->CR & ADC_CR_ADCAL) != 0) - ; -#if STM32_ADC_DUAL_MODE - osalDbgAssert(adcp->adcs->CR == ADC_CR_ADVREGEN_0, "invalid register state"); - adcp->adcs->CR |= ADC_CR_ADCAL; - while ((adcp->adcs->CR & ADC_CR_ADCAL) != 0) - ; -#endif -#endif - -#if defined(STM32L4XX) - osalDbgAssert(adcp->adcm->CR == ADC_CR_ADVREGEN, "invalid register state"); - adcp->adcm->CR |= ADC_CR_ADCAL; - while ((adcp->adcm->CR & ADC_CR_ADCAL) != 0) - ; -#if STM32_ADC_DUAL_MODE - osalDbgAssert(adcp->adcs->CR == ADC_CR_ADVREGEN, "invalid register state"); - adcp->adcs->CR |= ADC_CR_ADCAL; - while ((adcp->adcs->CR & ADC_CR_ADCAL) != 0) - ; -#endif -#endif -} - -/** - * @brief Stops an ongoing conversion, if any. - * - * @param[in] adcp pointer to the @p ADCDriver object - */ -static void adc_lld_stop_adc(ADCDriver *adcp) { - - if (adcp->adcm->CR & ADC_CR_ADSTART) { - adcp->adcm->CR |= ADC_CR_ADSTP; - while (adcp->adcm->CR & ADC_CR_ADSTP) - ; - } -} - -/** - * @brief ADC DMA ISR service routine. - * - * @param[in] adcp pointer to the @p ADCDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void adc_lld_serve_dma_interrupt(ADCDriver *adcp, uint32_t flags) { - - /* DMA errors handling.*/ - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - /* DMA, this could help only if the DMA tries to access an unmapped - address space or violates alignment rules.*/ - _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); - } - else { - /* It is possible that the conversion group has already be reset by the - ADC error handler, in this case this interrupt is spurious.*/ - if (adcp->grpp != NULL) { - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _adc_isr_full_code(adcp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _adc_isr_half_code(adcp); - } - } - } -} - -/** - * @brief ADC ISR service routine. - * - * @param[in] adcp pointer to the @p ADCDriver object - * @param[in] isr content of the ISR register - */ -static void adc_lld_serve_interrupt(ADCDriver *adcp, uint32_t isr) { - - /* It could be a spurious interrupt caused by overflows after DMA disabling, - just ignore it in this case.*/ - if (adcp->grpp != NULL) { - /* Note, an overflow may occur after the conversion ended before the driver - is able to stop the ADC, this is why the DMA channel is checked too.*/ - if ((isr & ADC_ISR_OVR) && - (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) { - /* ADC overflow condition, this could happen only if the DMA is unable - to read data fast enough.*/ - _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW); - } - if (isr & ADC_ISR_AWD1) { - /* Analog watchdog error.*/ - _adc_isr_error_code(adcp, ADC_ERR_AWD1); - } - if (isr & ADC_ISR_AWD2) { - /* Analog watchdog error.*/ - _adc_isr_error_code(adcp, ADC_ERR_AWD2); - } - if (isr & ADC_ISR_AWD3) { - /* Analog watchdog error.*/ - _adc_isr_error_code(adcp, ADC_ERR_AWD3); - } - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) -/** - * @brief ADC1/ADC2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) { - uint32_t isr; - - OSAL_IRQ_PROLOGUE(); - -#if STM32_ADC_DUAL_MODE - isr = ADC1->ISR; - isr |= ADC2->ISR; - ADC1->ISR = isr; - ADC2->ISR = isr; - - adc_lld_serve_interrupt(&ADCD1, isr); -#else /* !STM32_ADC_DUAL_MODE */ -#if STM32_ADC_USE_ADC1 - isr = ADC1->ISR; - ADC1->ISR = isr; - - adc_lld_serve_interrupt(&ADCD1, isr); -#endif -#if STM32_ADC_USE_ADC2 - isr = ADC2->ISR; - ADC2->ISR = isr; - - adc_lld_serve_interrupt(&ADCD2, isr); -#endif -#endif /* !STM32_ADC_DUAL_MODE */ - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_ADC_USE_ADC1 */ - -#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) -/** - * @brief ADC3 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_ADC3_HANDLER) { - uint32_t isr; - - OSAL_IRQ_PROLOGUE(); - - isr = ADC3->ISR; - ADC3->ISR = isr; - - adc_lld_serve_interrupt(&ADCD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#if STM32_ADC_DUAL_MODE -/** - * @brief ADC4 interrupt handler (as ADC3 slave). - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_ADC4_HANDLER) { - uint32_t isr; - - OSAL_IRQ_PROLOGUE(); - - isr = ADC4->ISR; - ADC4->ISR = isr; - - adc_lld_serve_interrupt(&ADCD3, isr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_ADC_DUAL_MODE */ -#endif /* STM32_ADC_USE_ADC3 */ - -#if STM32_ADC_USE_ADC4 || defined(__DOXYGEN__) -/** - * @brief ADC4 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_ADC4_HANDLER) { - uint32_t isr; - - OSAL_IRQ_PROLOGUE(); - - isr = ADC4->ISR; - ADC4->ISR = isr; - - adc_lld_serve_interrupt(&ADCD4, isr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_ADC_USE_ADC4 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ADC driver initialization. - * - * @notapi - */ -void adc_lld_init(void) { - - clkmask = 0; - -#if STM32_ADC_USE_ADC1 - /* Driver initialization.*/ - adcObjectInit(&ADCD1); -#if defined(ADC1_2_COMMON) - ADCD1.adcc = ADC1_2_COMMON; -#elif defined(ADC123_COMMON) - ADCD1.adcc = ADC123_COMMON; -#else - ADCD1.adcc = ADC1_COMMON; -#endif - ADCD1.adcm = ADC1; -#if STM32_ADC_DUAL_MODE - ADCD1.adcs = ADC2; -#endif - ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); - ADCD1.dmamode = ADC_DMA_SIZE | - STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif /* STM32_ADC_USE_ADC1 */ - -#if STM32_ADC_USE_ADC2 - /* Driver initialization.*/ - adcObjectInit(&ADCD2); -#if defined(ADC1_2_COMMON) - ADCD2.adcc = ADC1_2_COMMON; -#elif defined(ADC123_COMMON) - ADCD2.adcc = ADC123_COMMON; -#endif - ADCD2.adcm = ADC2; - ADCD2.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC2_DMA_STREAM); - ADCD2.dmamode = ADC_DMA_SIZE | - STM32_DMA_CR_PL(STM32_ADC_ADC2_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif /* STM32_ADC_USE_ADC2 */ - -#if STM32_ADC_USE_ADC3 - /* Driver initialization.*/ - adcObjectInit(&ADCD3); -#if defined(ADC3_4_COMMON) - ADCD3.adcc = ADC3_4_COMMON; -#elif defined(ADC123_COMMON) - ADCD1.adcc = ADC123_COMMON; -#else - ADCD3.adcc = ADC3_COMMON; -#endif - ADCD3.adcm = ADC3; -#if STM32_ADC_DUAL_MODE - ADCD3.adcs = ADC4; -#endif - ADCD3.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC3_DMA_STREAM); - ADCD3.dmamode = ADC_DMA_SIZE | - STM32_DMA_CR_PL(STM32_ADC_ADC3_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif /* STM32_ADC_USE_ADC3 */ - -#if STM32_ADC_USE_ADC4 - /* Driver initialization.*/ - adcObjectInit(&ADCD4); - ADCD4.adcc = ADC3_4_COMMON; - ADCD4.adcm = ADC4; - ADCD4.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC4_DMA_STREAM); - ADCD4.dmamode = ADC_DMA_SIZE | - STM32_DMA_CR_PL(STM32_ADC_ADC4_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; -#endif /* STM32_ADC_USE_ADC4 */ - - /* IRQs setup.*/ -#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 - nvicEnableVector(STM32_ADC1_NUMBER, STM32_ADC_ADC12_IRQ_PRIORITY); -#endif -#if STM32_ADC_USE_ADC3 - nvicEnableVector(STM32_ADC3_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); -#if STM32_ADC_DUAL_MODE - nvicEnableVector(STM32_ADC4_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); -#endif -#endif -#if STM32_ADC_USE_ADC4 - nvicEnableVector(STM32_ADC4_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); -#endif - - /* ADC units pre-initializations.*/ -#if defined(STM32F3XX) -#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 - rccEnableADC12(FALSE); - rccResetADC12(); - ADC1_2_COMMON->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; - rccDisableADC12(FALSE); -#endif -#if STM32_ADC_USE_ADC3 || STM32_ADC_USE_ADC4 - rccEnableADC34(FALSE); - rccResetADC34(); - ADC3_4_COMMON->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; - rccDisableADC34(FALSE); -#endif -#endif - -#if defined(STM32L4XX) - rccEnableADC123(FALSE); - rccResetADC123(); - ADC123_COMMON->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; - rccDisableADC123(FALSE); -#endif -} - -/** - * @brief Configures and activates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start(ADCDriver *adcp) { - - /* Handling the default configuration.*/ - if (adcp->config == NULL) { - adcp->config = &default_config; - } - - /* If in stopped state then enables the ADC and DMA clocks.*/ - if (adcp->state == ADC_STOP) { -#if STM32_ADC_USE_ADC1 - if (&ADCD1 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC1_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - - clkmask |= (1 << 0); -#if defined(STM32F3XX) - rccEnableADC12(FALSE); -#endif -#if defined(STM32L4XX) - rccEnableADC123(FALSE); -#endif - } -#endif /* STM32_ADC_USE_ADC1 */ - -#if STM32_ADC_USE_ADC2 - if (&ADCD2 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC2_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - - clkmask |= (1 << 1); -#if defined(STM32F3XX) - rccEnableADC12(FALSE); -#endif -#if defined(STM32L4XX) - rccEnableADC123(FALSE); -#endif - } -#endif /* STM32_ADC_USE_ADC2 */ - -#if STM32_ADC_USE_ADC3 - if (&ADCD3 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC3_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - - clkmask |= (1 << 2); -#if defined(STM32F3XX) - rccEnableADC34(FALSE); -#endif -#if defined(STM32L4XX) - rccEnableADC123(FALSE); -#endif - } -#endif /* STM32_ADC_USE_ADC3 */ - -#if STM32_ADC_USE_ADC4 - if (&ADCD4 == adcp) { - bool b; - b = dmaStreamAllocate(adcp->dmastp, - STM32_ADC_ADC4_DMA_IRQ_PRIORITY, - (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, - (void *)adcp); - osalDbgAssert(!b, "stream already allocated"); - - clkmask |= (1 << 3); -#if defined(STM32F3XX) - rccEnableADC34(FALSE); -#endif -#if defined(STM32L4XX) - rccEnableADC123(FALSE); -#endif - } -#endif /* STM32_ADC_USE_ADC4 */ - - /* Setting DMA peripheral-side pointer.*/ -#if STM32_ADC_DUAL_MODE - dmaStreamSetPeripheral(adcp->dmastp, &adcp->adcc->CDR); -#else - dmaStreamSetPeripheral(adcp->dmastp, &adcp->adcm->DR); -#endif - - /* Differential channels setting.*/ -#if STM32_ADC_DUAL_MODE - adcp->adcm->DIFSEL = adcp->config->difsel; - adcp->adcs->DIFSEL = adcp->config->difsel; -#else - adcp->adcm->DIFSEL = adcp->config->difsel; -#endif - - /* Master ADC calibration.*/ - adc_lld_vreg_on(adcp); - adc_lld_calibrate(adcp); - - /* Master ADC enabled here in order to reduce conversions latencies.*/ - adc_lld_analog_on(adcp); - } -} - -/** - * @brief Deactivates the ADC peripheral. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop(ADCDriver *adcp) { - - /* If in ready state then disables the ADC clock and analog part.*/ - if (adcp->state == ADC_READY) { - - /* Releasing the associated DMA channel.*/ - dmaStreamRelease(adcp->dmastp); - - /* Stopping the ongoing conversion, if any.*/ - adc_lld_stop_adc(adcp); - - /* Disabling ADC analog circuit and regulator.*/ - adc_lld_analog_off(adcp); - adc_lld_vreg_off(adcp); - -#if defined(STM32L4XX) - /* Resetting CCR options except default ones.*/ - adcp->adcc->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; -#endif - -#if STM32_ADC_USE_ADC1 - if (&ADCD1 == adcp) { -#if defined(STM32F3XX) - /* Resetting CCR options except default ones.*/ - adcp->adcc->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; -#endif - clkmask &= ~(1 << 0); - } -#endif - -#if STM32_ADC_USE_ADC2 - if (&ADCD1 == adcp) { -#if defined(STM32F3XX) - /* Resetting CCR options except default ones.*/ - adcp->adcc->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; -#endif - clkmask &= ~(1 << 1); - } -#endif - -#if STM32_ADC_USE_ADC3 - if (&ADCD1 == adcp) { -#if defined(STM32F3XX) - /* Resetting CCR options except default ones.*/ - adcp->adcc->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; -#endif - clkmask &= ~(1 << 2); - } -#endif - -#if STM32_ADC_USE_ADC4 - if (&ADCD1 == adcp) { -#if defined(STM32F3XX) - /* Resetting CCR options except default ones.*/ - adcp->adcc->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; -#endif - clkmask &= ~(1 << 3); - } -#endif - -#if defined(STM32F3XX) - if ((clkmask & 0x3) == 0) { - rccDisableADC12(FALSE); - } - if ((clkmask & 0xC) == 0) { - rccDisableADC34(FALSE); - } -#endif - -#if defined(STM32L4XX) - if ((clkmask & 0x7) == 0) { - rccDisableADC123(FALSE); - } -#endif - } -} - -/** - * @brief Starts an ADC conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_start_conversion(ADCDriver *adcp) { - uint32_t dmamode, cfgr; - const ADCConversionGroup *grpp = adcp->grpp; -#if STM32_ADC_DUAL_MODE - uint32_t ccr = grpp->ccr & ~(ADC_CCR_CKMODE_MASK | ADC_CCR_MDMA_MASK); -#endif - - osalDbgAssert(!STM32_ADC_DUAL_MODE || ((grpp->num_channels & 1) == 0), - "odd number of channels in dual mode"); - - /* Calculating control registers values.*/ - dmamode = adcp->dmamode; - cfgr = grpp->cfgr | ADC_CFGR_DMAEN; - if (grpp->circular) { - dmamode |= STM32_DMA_CR_CIRC; -#if STM32_ADC_DUAL_MODE - ccr |= ADC_CCR_DMACFG_CIRCULAR; -#else - cfgr |= ADC_CFGR_DMACFG_CIRCULAR; -#endif - if (adcp->depth > 1) { - /* If circular buffer depth > 1, then the half transfer interrupt - is enabled in order to allow streaming processing.*/ - dmamode |= STM32_DMA_CR_HTIE; - } - } - - /* DMA setup.*/ - dmaStreamSetMemory0(adcp->dmastp, adcp->samples); -#if STM32_ADC_DUAL_MODE - dmaStreamSetTransactionSize(adcp->dmastp, ((uint32_t)grpp->num_channels/2) * - (uint32_t)adcp->depth); -#else - dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * - (uint32_t)adcp->depth); -#endif - dmaStreamSetMode(adcp->dmastp, dmamode); - dmaStreamEnable(adcp->dmastp); - - /* ADC setup, if it is defined a callback for the analog watch dog then it - is enabled.*/ - adcp->adcm->ISR = adcp->adcm->ISR; - adcp->adcm->IER = ADC_IER_OVR | ADC_IER_AWD1; - adcp->adcm->TR1 = grpp->tr1; -#if STM32_ADC_DUAL_MODE - - /* Configuring the CCR register with the user-specified settings - in the conversion group configuration structure, static settings are - preserved.*/ - adcp->adcc->CCR = (adcp->adcc->CCR & - (ADC_CCR_CKMODE_MASK | ADC_CCR_MDMA_MASK)) | ccr; - - adcp->adcm->SMPR1 = grpp->smpr[0]; - adcp->adcm->SMPR2 = grpp->smpr[1]; - adcp->adcm->SQR1 = grpp->sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels / 2); - adcp->adcm->SQR2 = grpp->sqr[1]; - adcp->adcm->SQR3 = grpp->sqr[2]; - adcp->adcm->SQR4 = grpp->sqr[3]; - adcp->adcs->SMPR1 = grpp->ssmpr[0]; - adcp->adcs->SMPR2 = grpp->ssmpr[1]; - adcp->adcs->SQR1 = grpp->ssqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels / 2); - adcp->adcs->SQR2 = grpp->ssqr[1]; - adcp->adcs->SQR3 = grpp->ssqr[2]; - adcp->adcs->SQR4 = grpp->ssqr[3]; - -#else /* !STM32_ADC_DUAL_MODE */ - adcp->adcm->SMPR1 = grpp->smpr[0]; - adcp->adcm->SMPR2 = grpp->smpr[1]; - adcp->adcm->SQR1 = grpp->sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels); - adcp->adcm->SQR2 = grpp->sqr[1]; - adcp->adcm->SQR3 = grpp->sqr[2]; - adcp->adcm->SQR4 = grpp->sqr[3]; -#endif /* !STM32_ADC_DUAL_MODE */ - - /* ADC configuration.*/ - adcp->adcm->CFGR = cfgr; - - /* Starting conversion.*/ - adcp->adcm->CR |= ADC_CR_ADSTART; -} - -/** - * @brief Stops an ongoing conversion. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adc_lld_stop_conversion(ADCDriver *adcp) { - - dmaStreamDisable(adcp->dmastp); - adc_lld_stop_adc(adcp); -} - -/** - * @brief Enables the VREFEN bit. - * @details The VREFEN bit is required in order to sample the VREF channel. - * @note This is an STM32-only functionality. - * @note This function is meant to be called after @p adcStart(). - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adcSTM32EnableVREF(ADCDriver *adcp) { - - adcp->adcc->CCR |= ADC_CCR_VBATEN; -} - -/** - * @brief Disables the VREFEN bit. - * @details The VREFEN bit is required in order to sample the VREF channel. - * @note This is an STM32-only functionality. - * @note This function is meant to be called after @p adcStart(). - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adcSTM32DisableVREF(ADCDriver *adcp) { - - adcp->adcc->CCR &= ~ADC_CCR_VBATEN; -} - -/** - * @brief Enables the TSEN bit. - * @details The TSEN bit is required in order to sample the internal - * temperature sensor and internal reference voltage. - * @note This is an STM32-only functionality. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adcSTM32EnableTS(ADCDriver *adcp) { - - adcp->adcc->CCR |= ADC_CCR_TSEN; -} - -/** - * @brief Disables the TSEN bit. - * @details The TSEN bit is required in order to sample the internal - * temperature sensor and internal reference voltage. - * @note This is an STM32-only functionality. - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adcSTM32DisableTS(ADCDriver *adcp) { - - adcp->adcc->CCR &= ~ADC_CCR_TSEN; -} - -/** - * @brief Enables the VBATEN bit. - * @details The VBATEN bit is required in order to sample the VBAT channel. - * @note This is an STM32-only functionality. - * @note This function is meant to be called after @p adcStart(). - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adcSTM32EnableVBAT(ADCDriver *adcp) { - - adcp->adcc->CCR |= ADC_CCR_VBATEN; -} - -/** - * @brief Disables the VBATEN bit. - * @details The VBATEN bit is required in order to sample the VBAT channel. - * @note This is an STM32-only functionality. - * @note This function is meant to be called after @p adcStart(). - * - * @param[in] adcp pointer to the @p ADCDriver object - * - * @notapi - */ -void adcSTM32DisableVBAT(ADCDriver *adcp) { - - adcp->adcc->CCR &= ~ADC_CCR_VBATEN; -} - -#endif /* HAL_USE_ADC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv3/adc_lld.h b/os/hal/ports/STM32/LLD/ADCv3/adc_lld.h deleted file mode 100644 index 8e5a02c15..000000000 --- a/os/hal/ports/STM32/LLD/ADCv3/adc_lld.h +++ /dev/null @@ -1,888 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32F3xx/adc_lld.h - * @brief STM32F3xx ADC subsystem low level driver header. - * - * @addtogroup ADC - * @{ - */ - -#ifndef _ADC_LLD_H_ -#define _ADC_LLD_H_ - -#if HAL_USE_ADC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Available analog channels - * @{ - */ -#define ADC_CHANNEL_IN0 0 /**< @brief External analog input 0. */ -#define ADC_CHANNEL_IN1 1 /**< @brief External analog input 1. */ -#define ADC_CHANNEL_IN2 2 /**< @brief External analog input 2. */ -#define ADC_CHANNEL_IN3 3 /**< @brief External analog input 3. */ -#define ADC_CHANNEL_IN4 4 /**< @brief External analog input 4. */ -#define ADC_CHANNEL_IN5 5 /**< @brief External analog input 5. */ -#define ADC_CHANNEL_IN6 6 /**< @brief External analog input 6. */ -#define ADC_CHANNEL_IN7 7 /**< @brief External analog input 7. */ -#define ADC_CHANNEL_IN8 8 /**< @brief External analog input 8. */ -#define ADC_CHANNEL_IN9 9 /**< @brief External analog input 9. */ -#define ADC_CHANNEL_IN10 10 /**< @brief External analog input 10. */ -#define ADC_CHANNEL_IN11 11 /**< @brief External analog input 11. */ -#define ADC_CHANNEL_IN12 12 /**< @brief External analog input 12. */ -#define ADC_CHANNEL_IN13 13 /**< @brief External analog input 13. */ -#define ADC_CHANNEL_IN14 14 /**< @brief External analog input 14. */ -#define ADC_CHANNEL_IN15 15 /**< @brief External analog input 15. */ -#define ADC_CHANNEL_IN16 16 /**< @brief External analog input 16. */ -#define ADC_CHANNEL_IN17 17 /**< @brief External analog input 17. */ -#define ADC_CHANNEL_IN18 18 /**< @brief External analog input 18. */ -/** @} */ - -/** - * @name Sampling rates - * @{ - */ -#if defined(STM32F3XX) -#define ADC_SMPR_SMP_1P5 0 /**< @brief 14 cycles conversion time */ -#define ADC_SMPR_SMP_2P5 1 /**< @brief 15 cycles conversion time. */ -#define ADC_SMPR_SMP_4P5 2 /**< @brief 17 cycles conversion time. */ -#define ADC_SMPR_SMP_7P5 3 /**< @brief 20 cycles conversion time. */ -#define ADC_SMPR_SMP_19P5 4 /**< @brief 32 cycles conversion time. */ -#define ADC_SMPR_SMP_61P5 5 /**< @brief 74 cycles conversion time. */ -#define ADC_SMPR_SMP_181P5 6 /**< @brief 194 cycles conversion time. */ -#define ADC_SMPR_SMP_601P5 7 /**< @brief 614 cycles conversion time. */ -#endif -#if defined(STM32L4XX) -#define ADC_SMPR_SMP_2P5 0 /**< @brief 15 cycles conversion time */ -#define ADC_SMPR_SMP_6P5 1 /**< @brief 19 cycles conversion time. */ -#define ADC_SMPR_SMP_12P5 2 /**< @brief 25 cycles conversion time. */ -#define ADC_SMPR_SMP_24P5 3 /**< @brief 37 cycles conversion time. */ -#define ADC_SMPR_SMP_47P5 4 /**< @brief 60 cycles conversion time. */ -#define ADC_SMPR_SMP_92P5 5 /**< @brief 105 cycles conversion time. */ -#define ADC_SMPR_SMP_247P5 6 /**< @brief 260 cycles conversion time. */ -#define ADC_SMPR_SMP_640P5 7 /**< @brief 653 cycles conversion time. */ -#endif -/** @} */ - -/** - * @name Resolution - * @{ - */ -#define ADC_CFGR1_RES_12BIT (0 << 3) -#define ADC_CFGR1_RES_10BIT (1 << 3) -#define ADC_CFGR1_RES_8BIT (2 << 3) -#define ADC_CFGR1_RES_6BIT (3 << 3) -/** @} */ - -/** - * @name CFGR register configuration helpers - * @{ - */ -#define ADC_CFGR_DMACFG_MASK (1 << 1) -#define ADC_CFGR_DMACFG_ONESHOT (0 << 1) -#define ADC_CFGR_DMACFG_CIRCULAR (1 << 1) - -#define ADC_CFGR_RES_MASK (3 << 3) -#define ADC_CFGR_RES_12BITS (0 << 3) -#define ADC_CFGR_RES_10BITS (1 << 3) -#define ADC_CFGR_RES_8BITS (2 << 3) -#define ADC_CFGR_RES_6BITS (3 << 3) - -#define ADC_CFGR_ALIGN_MASK (1 << 5) -#define ADC_CFGR_ALIGN_RIGHT (0 << 5) -#define ADC_CFGR_ALIGN_LEFT (1 << 5) - -#define ADC_CFGR_EXTSEL_MASK (15 << 6) -#define ADC_CFGR_EXTSEL_SRC(n) ((n) << 6) - -#define ADC_CFGR_EXTEN_MASK (3 << 10) -#define ADC_CFGR_EXTEN_DISABLED (0 << 10) -#define ADC_CFGR_EXTEN_RISING (1 << 10) -#define ADC_CFGR_EXTEN_FALLING (2 << 10) -#define ADC_CFGR_EXTEN_BOTH (3 << 10) - -#define ADC_CFGR_DISCEN_MASK (1 << 16) -#define ADC_CFGR_DISCEN_DISABLED (0 << 16) -#define ADC_CFGR_DISCEN_ENABLED (1 << 16) - -#define ADC_CFGR_DISCNUM_MASK (7 << 17) -#define ADC_CFGR_DISCNUM_VAL(n) ((n) << 17) - -#define ADC_CFGR_AWD1_DISABLED 0 -#define ADC_CFGR_AWD1_ALL (1 << 23) -#define ADC_CFGR_AWD1_SINGLE(n) (((n) << 26) | (1 << 23) | (1 << 22)) -/** @} */ - -/** - * @name CCR register configuration helpers - * @{ - */ -#define ADC_CCR_DUAL_MASK (31 << 0) -#define ADC_CCR_DUAL_FIELD(n) ((n) << 0) -#define ADC_CCR_DELAY_MASK (15 << 8) -#define ADC_CCR_DELAY_FIELD(n) ((n) << 8) -#define ADC_CCR_DMACFG_MASK (1 << 13) -#define ADC_CCR_DMACFG_ONESHOT (0 << 13) -#define ADC_CCR_DMACFG_CIRCULAR (1 << 13) -#define ADC_CCR_MDMA_MASK (3 << 14) -#define ADC_CCR_MDMA_DISABLED (0 << 14) -#define ADC_CCR_MDMA_WORD (2 << 14) -#define ADC_CCR_MDMA_HWORD (3 << 14) -#define ADC_CCR_CKMODE_MASK (3 << 16) -#define ADC_CCR_CKMODE_ADCCK (0 << 16) -#define ADC_CCR_CKMODE_AHB_DIV1 (1 << 16) -#define ADC_CCR_CKMODE_AHB_DIV2 (2 << 16) -#define ADC_CCR_CKMODE_AHB_DIV4 (3 << 16) - -/* F3 headers do not define the following macros, L4 headers do.*/ -#if !defined(ADC_CCR_VREFEN) || defined(__DOXYGEN__) -#define ADC_CCR_VREFEN (1 << 22) -#endif - -#if !defined(ADC_CCR_TSEN) || defined(__DOXYGEN__) -#define ADC_CCR_TSEN (1 << 23) -#endif - -#if !defined(ADC_CCR_VBATEN) || defined(__DOXYGEN__) -#define ADC_CCR_VBATEN (1 << 24) -#endif -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief Enables the ADC master/slave mode. - * @note In dual mode only ADCD1 and ADCD3 are available. - */ -#if !defined(STM32_ADC_DUAL_MODE) || defined(__DOXYGEN__) -#define STM32_ADC_DUAL_MODE FALSE -#endif - -/** - * @brief Makes the ADC samples type an 8bits one. - * @note 10 and 12 bits sampling mode must not be used when this option - * is enabled. - */ -#if !defined(STM32_ADC_COMPACT_SAMPLES) || defined(__DOXYGEN__) -#define STM32_ADC_COMPACT_SAMPLES FALSE -#endif - -/** - * @brief ADC1 driver enable switch. - * @details If set to @p TRUE the support for ADC1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC1 FALSE -#endif - -/** - * @brief ADC2 driver enable switch. - * @details If set to @p TRUE the support for ADC2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_ADC_USE_ADC2) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC2 FALSE -#endif - -/** - * @brief ADC3 driver enable switch. - * @details If set to @p TRUE the support for ADC3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_ADC_USE_ADC3) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC3 FALSE -#endif -/** - * @brief ADC4 driver enable switch. - * @details If set to @p TRUE the support for ADC4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_ADC_USE_ADC4) || defined(__DOXYGEN__) -#define STM32_ADC_USE_ADC4 FALSE -#endif - -/** - * @brief ADC1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC2_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC3 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC3_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC4 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_ADC_ADC4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC4_DMA_PRIORITY 2 -#endif - -/** - * @brief ADC1/ADC2 interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC12_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC12_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC3 interrupt priority level setting. - */ -#if !defined(STM32_ADC3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC3_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC4 interrupt priority level setting. - */ -#if !defined(STM32_ADC4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC4_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC1 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC2 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC2_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC3 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY 5 -#endif - -/** - * @brief ADC4 DMA interrupt priority level setting. - */ -#if !defined(STM32_ADC_ADC4_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ADC_ADC4_DMA_IRQ_PRIORITY 5 -#endif - -#if defined(STM32F3XX) || defined(__DOXYGEN__) -/** - * @brief ADC1/ADC2 clock source and mode. - */ -#if !defined(STM32_ADC_ADC12_CLOCK_MODE) || defined(__DOXYGEN__) -#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 -#endif - -/** - * @brief ADC3/ADC4 clock source and mode. - */ -#if !defined(STM32_ADC_ADC34_CLOCK_MODE) || defined(__DOXYGEN__) -#define STM32_ADC_ADC34_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 -#endif -#endif /* defined(STM32F3XX) */ - -#if defined(STM32L4XX) || defined(__DOXYGEN__) -/** - * @brief ADC1/ADC2/ADC3 clock source and mode. - */ -#if !defined(STM32_ADC_ADC123_CLOCK_MODE) || defined(__DOXYGEN__) -#define STM32_ADC_ADC123_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 -#endif -#endif /* defined(STM32L4XX) */ - -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/* Supported devices checks.*/ -#if !defined(STM32F3XX) && !defined(STM32L4XX) -#error "ADCv3 only supports F3 and L4 STM32 devices" -#endif - -/* Registry checks.*/ -#if !defined(STM32_HAS_ADC1) || !defined(STM32_HAS_ADC2) || \ - !defined(STM32_HAS_ADC3) || !defined(STM32_HAS_ADC4) -#error "STM32_ADC_USE_ADCx not defined in registry" -#endif - -#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_HANDLER)) || \ - (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_HANDLER)) || \ - (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_HANDLER)) || \ - (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_HANDLER)) -#error "STM32_ADCx_HANDLER not defined in registry" -#endif - -#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_NUMBER)) || \ - (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_NUMBER)) || \ - (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_NUMBER)) || \ - (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_NUMBER)) -#error "STM32_ADCx_NUMBER not defined in registry" -#endif - -#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_DMA_MSK)) || \ - (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_DMA_MSK)) || \ - (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_DMA_MSK)) || \ - (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_DMA_MSK)) -#error "STM32_ADCx_DMA_MSK not defined in registry" -#endif - -#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_DMA_CHN)) || \ - (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_DMA_CHN)) || \ - (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_DMA_CHN)) || \ - (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_DMA_CHN)) -#error "STM32_ADCx_DMA_CHN not defined in registry" -#endif - -/* Units checks.*/ -#if STM32_ADC_USE_ADC1 && !STM32_HAS_ADC1 -#error "ADC1 not present in the selected device" -#endif - -#if STM32_ADC_USE_ADC2 && !STM32_HAS_ADC2 -#error "ADC2 not present in the selected device" -#endif - -#if STM32_ADC_USE_ADC3 && !STM32_HAS_ADC3 -#error "ADC3 not present in the selected device" -#endif - -#if STM32_ADC_USE_ADC4 && !STM32_HAS_ADC4 -#error "ADC4 not present in the selected device" -#endif - -/* Units checks related to dual mode.*/ -#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC1 && !STM32_HAS_ADC2 -#error "ADC2 not present in the selected device, required for dual mode" -#endif - -#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC3 && !STM32_HAS_ADC4 -#error "ADC4 not present in the selected device, required for dual mode" -#endif - -#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC2 -#error "ADC2 cannot be used in dual mode" -#endif - -#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC4 -#error "ADC4 cannot be used in dual mode" -#endif - -/* At least one ADC must be assigned.*/ -#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && \ - !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 -#error "ADC driver activated but no ADC peripheral assigned" -#endif - -/* ISR arrangments checks.*/ -#if STM32_ADC1_NUMBER != STM32_ADC2_NUMBER -#error "ADCv3 driver expects STM32_ADC1_NUMBER == STM32_ADC2_NUMBER from registry" -#endif - -/* ADC IRQ priority tests.*/ -#if STM32_ADC_USE_ADC1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC12_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC1" -#endif - -/* ADC IRQ priority tests.*/ -#if STM32_ADC_USE_ADC2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC12_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC2" -#endif - -#if STM32_ADC_USE_ADC3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC3" -#endif - -#if STM32_ADC_USE_ADC4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC4" -#endif - -/* DMA IRQ priority tests.*/ -#if STM32_ADC_USE_ADC1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC1 DMA" -#endif - -#if STM32_ADC_USE_ADC2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC2_DMA_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC2 DMA" -#endif - -#if STM32_ADC_USE_ADC3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC3 DMA" -#endif - -#if STM32_ADC_USE_ADC4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC4_DMA_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to ADC4 DMA" -#endif - -/* DMA priority tests.*/ -#if STM32_ADC_USE_ADC1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to ADC1" -#endif - -#if STM32_ADC_USE_ADC2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to ADC2" -#endif - -#if STM32_ADC_USE_ADC3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to ADC3" -#endif - -#if STM32_ADC_USE_ADC4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to ADC4" -#endif - -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_ADC_USE_ADC1 && !defined(STM32_ADC_ADC1_DMA_STREAM) -#error "ADC1 DMA stream not defined" -#endif - -#if STM32_ADC_USE_ADC2 && !defined(STM32_ADC_ADC2_DMA_STREAM) -#error "ADC2 DMA stream not defined" -#endif - -#if STM32_ADC_USE_ADC3 && !defined(STM32_ADC_ADC3_DMA_STREAM) -#error "ADC3 DMA stream not defined" -#endif - -#if STM32_ADC_USE_ADC4 && !defined(STM32_ADC_ADC4_DMA_STREAM) -#error "ADC4 DMA stream not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_ADC_USE_ADC1 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK) -#error "invalid DMA stream associated to ADC1" -#endif - -#if STM32_ADC_USE_ADC2 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_MSK) -#error "invalid DMA stream associated to ADC2" -#endif - -#if STM32_ADC_USE_ADC3 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_MSK) -#error "invalid DMA stream associated to ADC3" -#endif - -#if STM32_ADC_USE_ADC4 && \ - !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC4_DMA_STREAM, STM32_ADC4_DMA_MSK) -#error "invalid DMA stream associated to ADC4" -#endif - -/* ADC clock source checks.*/ -#if defined(STM32F3XX) -#if STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK -#define STM32_ADC12_CLOCK STM32_ADC12CLK -#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1 -#define STM32_ADC12_CLOCK (STM32_HCLK / 1) -#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2 -#define STM32_ADC12_CLOCK (STM32_HCLK / 2) -#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4 -#define STM32_ADC12_CLOCK (STM32_HCLK / 4) -#else -#error "invalid clock mode selected for STM32_ADC_ADC12_CLOCK_MODE" -#endif - -#if STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK -#define STM32_ADC34_CLOCK STM32_ADC34CLK -#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1 -#define STM32_ADC34_CLOCK (STM32_HCLK / 1) -#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2 -#define STM32_ADC34_CLOCK (STM32_HCLK / 2) -#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4 -#define STM32_ADC34_CLOCK (STM32_HCLK / 4) -#else -#error "invalid clock mode selected for STM32_ADC_ADC12_CLOCK_MODE" -#endif - -#if STM32_ADC12_CLOCK > STM32_ADCCLK_MAX -#error "STM32_ADC12_CLOCK exceeding maximum frequency (STM32_ADCCLK_MAX)" -#endif - -#if STM32_ADC34_CLOCK > STM32_ADCCLK_MAX -#error "STM32_ADC34_CLOCK exceeding maximum frequency (STM32_ADCCLK_MAX)" -#endif -#endif /* defined(STM32F3XX) */ - -#if defined(STM32L4XX) -#if STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK -#define STM32_ADC123_CLOCK STM32_ADC12CLK -#elif STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1 -#define STM32_ADC123_CLOCK (STM32_HCLK / 1) -#elif STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2 -#define STM32_ADC123_CLOCK (STM32_HCLK / 2) -#elif STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4 -#define STM32_ADC123_CLOCK (STM32_HCLK / 4) -#else -#error "invalid clock mode selected for STM32_ADC_ADC123_CLOCK_MODE" -#endif - -#if STM32_ADC123_CLOCK > STM32_ADCCLK_MAX -#error "STM32_ADC123_CLOCK exceeding maximum frequency (STM32_ADCCLK_MAX)" -#endif -#endif /* defined(STM32L4XX) */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief ADC sample data type. - */ -#if !STM32_ADC_COMPACT_SAMPLES || defined(__DOXYGEN__) -typedef uint16_t adcsample_t; -#else -typedef uint8_t adcsample_t; -#endif - -/** - * @brief Channels number in a conversion group. - */ -typedef uint16_t adc_channels_num_t; - -/** - * @brief Possible ADC failure causes. - * @note Error codes are architecture dependent and should not relied - * upon. - */ -typedef enum { - ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ - ADC_ERR_OVERFLOW = 1, /**< ADC overflow condition. */ - ADC_ERR_AWD1 = 2, /**< Watchdog 1 triggered. */ - ADC_ERR_AWD2 = 3, /**< Watchdog 2 triggered. */ - ADC_ERR_AWD3 = 4 /**< Watchdog 3 triggered. */ -} adcerror_t; - -/** - * @brief Type of a structure representing an ADC driver. - */ -typedef struct ADCDriver ADCDriver; - -/** - * @brief ADC notification callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] buffer pointer to the most recent samples data - * @param[in] n number of buffer rows available starting from @p buffer - */ -typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); - -/** - * @brief ADC error callback type. - * - * @param[in] adcp pointer to the @p ADCDriver object triggering the - * callback - * @param[in] err ADC error code - */ -typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); - -/** - * @brief Conversion group configuration structure. - * @details This implementation-dependent structure describes a conversion - * operation. - * @note The use of this configuration structure requires knowledge of - * STM32 ADC cell registers interface, please refer to the STM32 - * reference manual for details. - */ -typedef struct { - /** - * @brief Enables the circular buffer mode for the group. - */ - bool circular; - /** - * @brief Number of the analog channels belonging to the conversion group. - */ - adc_channels_num_t num_channels; - /** - * @brief Callback function associated to the group or @p NULL. - */ - adccallback_t end_cb; - /** - * @brief Error callback or @p NULL. - */ - adcerrorcallback_t error_cb; - /* End of the mandatory fields.*/ - /** - * @brief ADC CFGR register initialization data. - * @note The bits DMAEN and DMACFG are enforced internally - * to the driver, keep them to zero. - * @note The bits @p ADC_CFGR_CONT or @p ADC_CFGR_DISCEN must be - * specified in continuous mode or if the buffer depth is - * greater than one. - */ - uint32_t cfgr; - /** - * @brief ADC TR1 register initialization data. - */ - uint32_t tr1; -#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__) - /** - * @brief ADC CCR register initialization data. - * @note The bits CKMODE, MDMA, DMACFG are enforced internally to the - * driver, keep them to zero. - * @note This field is only present in dual mode. - */ - uint32_t ccr; -#endif - /** - * @brief ADC SMPRx registers initialization data. - */ - uint32_t smpr[2]; - /** - * @brief ADC SQRx register initialization data. - */ - uint32_t sqr[4]; -#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__) - /** - * @brief Slave ADC SMPRx registers initialization data. - * @note This field is only present in dual mode. - */ - uint32_t ssmpr[2]; - /** - * @brief Slave ADC SQRx register initialization data. - * @note This field is only present in dual mode. - */ - uint32_t ssqr[4]; -#endif /* STM32_ADC_DUAL_MODE */ -} ADCConversionGroup; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /** - * @brief ADC DIFSEL register initialization data. - */ - uint32_t difsel; -} ADCConfig; - -/** - * @brief Structure representing an ADC driver. - */ -struct ADCDriver { - /** - * @brief Driver state. - */ - adcstate_t state; - /** - * @brief Current configuration data. - */ - const ADCConfig *config; - /** - * @brief Current samples buffer pointer or @p NULL. - */ - adcsample_t *samples; - /** - * @brief Current samples buffer depth or @p 0. - */ - size_t depth; - /** - * @brief Current conversion group pointer or @p NULL. - */ - const ADCConversionGroup *grpp; -#if ADC_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif -#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* ADC_USE_MUTUAL_EXCLUSION */ -#if defined(ADC_DRIVER_EXT_FIELDS) - ADC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the master ADCx registers block. - */ - ADC_TypeDef *adcm; -#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__) - /** - * @brief Pointer to the slave ADCx registers block. - */ - ADC_TypeDef *adcs; -#endif /* STM32_ADC_DUAL_MODE */ - /** - * @brief Pointer to the common ADCx_y registers block. - */ - ADC_Common_TypeDef *adcc; - /** - * @brief Pointer to associated DMA channel. - */ - const stm32_dma_stream_t *dmastp; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @name Threashold register initializer - * @{ - */ -#define ADC_TR(low, high) (((uint32_t)(high) << 16) | (uint32_t)(low)) -/** @} */ - -/** - * @name Sequences building helper macros - * @{ - */ -/** - * @brief Number of channels in a conversion sequence. - */ -#define ADC_SQR1_NUM_CH(n) (((n) - 1) << 0) - -#define ADC_SQR1_SQ1_N(n) ((n) << 6) /**< @brief 1st channel in seq. */ -#define ADC_SQR1_SQ2_N(n) ((n) << 12) /**< @brief 2nd channel in seq. */ -#define ADC_SQR1_SQ3_N(n) ((n) << 18) /**< @brief 3rd channel in seq. */ -#define ADC_SQR1_SQ4_N(n) ((n) << 24) /**< @brief 4th channel in seq. */ - -#define ADC_SQR2_SQ5_N(n) ((n) << 0) /**< @brief 5th channel in seq. */ -#define ADC_SQR2_SQ6_N(n) ((n) << 6) /**< @brief 6th channel in seq. */ -#define ADC_SQR2_SQ7_N(n) ((n) << 12) /**< @brief 7th channel in seq. */ -#define ADC_SQR2_SQ8_N(n) ((n) << 18) /**< @brief 8th channel in seq. */ -#define ADC_SQR2_SQ9_N(n) ((n) << 24) /**< @brief 9th channel in seq. */ - -#define ADC_SQR3_SQ10_N(n) ((n) << 0) /**< @brief 10th channel in seq.*/ -#define ADC_SQR3_SQ11_N(n) ((n) << 6) /**< @brief 11th channel in seq.*/ -#define ADC_SQR3_SQ12_N(n) ((n) << 12) /**< @brief 12th channel in seq.*/ -#define ADC_SQR3_SQ13_N(n) ((n) << 18) /**< @brief 13th channel in seq.*/ -#define ADC_SQR3_SQ14_N(n) ((n) << 24) /**< @brief 14th channel in seq.*/ - -#define ADC_SQR4_SQ15_N(n) ((n) << 0) /**< @brief 15th channel in seq.*/ -#define ADC_SQR4_SQ16_N(n) ((n) << 6) /**< @brief 16th channel in seq.*/ -/** @} */ - -/** - * @name Sampling rate settings helper macros - * @{ - */ -#define ADC_SMPR1_SMP_AN0(n) ((n) << 0) /**< @brief AN0 sampling time. */ -#define ADC_SMPR1_SMP_AN1(n) ((n) << 3) /**< @brief AN1 sampling time. */ -#define ADC_SMPR1_SMP_AN2(n) ((n) << 6) /**< @brief AN2 sampling time. */ -#define ADC_SMPR1_SMP_AN3(n) ((n) << 9) /**< @brief AN3 sampling time. */ -#define ADC_SMPR1_SMP_AN4(n) ((n) << 12) /**< @brief AN4 sampling time. */ -#define ADC_SMPR1_SMP_AN5(n) ((n) << 15) /**< @brief AN5 sampling time. */ -#define ADC_SMPR1_SMP_AN6(n) ((n) << 18) /**< @brief AN6 sampling time. */ -#define ADC_SMPR1_SMP_AN7(n) ((n) << 21) /**< @brief AN7 sampling time. */ -#define ADC_SMPR1_SMP_AN8(n) ((n) << 24) /**< @brief AN8 sampling time. */ -#define ADC_SMPR1_SMP_AN9(n) ((n) << 27) /**< @brief AN9 sampling time. */ - -#define ADC_SMPR2_SMP_AN10(n) ((n) << 0) /**< @brief AN10 sampling time. */ -#define ADC_SMPR2_SMP_AN11(n) ((n) << 3) /**< @brief AN11 sampling time. */ -#define ADC_SMPR2_SMP_AN12(n) ((n) << 6) /**< @brief AN12 sampling time. */ -#define ADC_SMPR2_SMP_AN13(n) ((n) << 9) /**< @brief AN13 sampling time. */ -#define ADC_SMPR2_SMP_AN14(n) ((n) << 12) /**< @brief AN14 sampling time. */ -#define ADC_SMPR2_SMP_AN15(n) ((n) << 15) /**< @brief AN15 sampling time. */ -#define ADC_SMPR2_SMP_AN16(n) ((n) << 18) /**< @brief AN16 sampling time. */ -#define ADC_SMPR2_SMP_AN17(n) ((n) << 21) /**< @brief AN17 sampling time. */ -#define ADC_SMPR2_SMP_AN18(n) ((n) << 24) /**< @brief AN18 sampling time. */ -/** @} */ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_ADC_USE_ADC1 && !defined(__DOXYGEN__) -extern ADCDriver ADCD1; -#endif - -#if STM32_ADC_USE_ADC2 && !defined(__DOXYGEN__) -extern ADCDriver ADCD2; -#endif - -#if STM32_ADC_USE_ADC3 && !defined(__DOXYGEN__) -extern ADCDriver ADCD3; -#endif - -#if STM32_ADC_USE_ADC4 && !defined(__DOXYGEN__) -extern ADCDriver ADCD4; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void adc_lld_init(void); - void adc_lld_start(ADCDriver *adcp); - void adc_lld_stop(ADCDriver *adcp); - void adc_lld_start_conversion(ADCDriver *adcp); - void adc_lld_stop_conversion(ADCDriver *adcp); - void adcSTM32EnableVREF(ADCDriver *adcp); - void adcSTM32DisableVREF(ADCDriver *adcp); - void adcSTM32EnableTS(ADCDriver *adcp); - void adcSTM32DisableTS(ADCDriver *adcp); - void adcSTM32EnableVBAT(ADCDriver *adcp); - void adcSTM32DisableVBAT(ADCDriver *adcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_ADC */ - -#endif /* _ADC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.c b/os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.c new file mode 100644 index 000000000..1e56336e0 --- /dev/null +++ b/os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.c @@ -0,0 +1,938 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32F3xx/adc_lld.c + * @brief STM32F3xx ADC subsystem low level driver source. + * + * @addtogroup ADC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define ADC1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_CHN) + +#define ADC2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_CHN) + +#define ADC3_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_CHN) + +#define ADC4_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_ADC_ADC4_DMA_STREAM, STM32_ADC4_DMA_CHN) + +#if STM32_ADC_DUAL_MODE +#if STM32_ADC_COMPACT_SAMPLES +/* Compact type dual mode.*/ +#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD) +#define ADC_DMA_MDMA ADC_CCR_MDMA_HWORD + +#else /* !STM32_ADC_COMPACT_SAMPLES */ +/* Large type dual mode.*/ +#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_PSIZE_WORD) +#define ADC_DMA_MDMA ADC_CCR_MDMA_WORD +#endif /* !STM32_ADC_COMPACT_SAMPLES */ + +#else /* !STM32_ADC_DUAL_MODE */ +#if STM32_ADC_COMPACT_SAMPLES +/* Compact type single mode.*/ +#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_BYTE | STM32_DMA_CR_PSIZE_BYTE) +#define ADC_DMA_MDMA ADC_CCR_MDMA_DISABLED + +#else /* !STM32_ADC_COMPACT_SAMPLES */ +/* Large type single mode.*/ +#define ADC_DMA_SIZE (STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_PSIZE_HWORD) +#define ADC_DMA_MDMA ADC_CCR_MDMA_DISABLED +#endif /* !STM32_ADC_COMPACT_SAMPLES */ +#endif /* !STM32_ADC_DUAL_MODE */ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief ADC1 driver identifier.*/ +#if STM32_ADC_USE_ADC1 || defined(__DOXYGEN__) +ADCDriver ADCD1; +#endif + +/** @brief ADC2 driver identifier.*/ +#if STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) +ADCDriver ADCD2; +#endif + +/** @brief ADC3 driver identifier.*/ +#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) +ADCDriver ADCD3; +#endif + +/** @brief ADC4 driver identifier.*/ +#if STM32_ADC_USE_ADC4 || defined(__DOXYGEN__) +ADCDriver ADCD4; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const ADCConfig default_config = { + difsel: 0 +}; + +static uint32_t clkmask; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Enables the ADC voltage regulator. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_vreg_on(ADCDriver *adcp) { + +#if defined(STM32F3XX) + adcp->adcm->CR = 0; /* RM 12.4.3.*/ + adcp->adcm->CR = ADC_CR_ADVREGEN_0; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = ADC_CR_ADVREGEN_0; +#endif + osalSysPolledDelayX(OSAL_US2RTC(STM32_HCLK, 10)); +#endif + +#if defined(STM32L4XX) + adcp->adcm->CR = 0; /* RM 16.3.6.*/ + adcp->adcm->CR = ADC_CR_ADVREGEN; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = ADC_CR_ADVREGEN; +#endif + osalSysPolledDelayX(OSAL_US2RTC(STM32_HCLK, 20)); +#endif +} + +/** + * @brief Disables the ADC voltage regulator. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_vreg_off(ADCDriver *adcp) { + +#if defined(STM32F3XX) + adcp->adcm->CR = 0; /* RM 12.4.3.*/ + adcp->adcm->CR = ADC_CR_ADVREGEN_1; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = 0; + adcp->adcs->CR = ADC_CR_ADVREGEN_1; +#endif +#endif + +#if defined(STM32L4XX) + adcp->adcm->CR = 0; /* RM 12.4.3.*/ + adcp->adcm->CR = ADC_CR_DEEPPWD; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR = 0; + adcp->adcs->CR = ADC_CR_DEEPPWD; +#endif +#endif +} + +/** + * @brief Enables the ADC analog circuit. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_analog_on(ADCDriver *adcp) { + +#if defined(STM32F3XX) + adcp->adcm->CR |= ADC_CR_ADEN; + while ((adcp->adcm->ISR & ADC_ISR_ADRD) == 0) + ; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR |= ADC_CR_ADEN; + while ((adcp->adcs->ISR & ADC_ISR_ADRD) == 0) + ; +#endif +#endif + +#if defined(STM32L4XX) + adcp->adcm->CR |= ADC_CR_ADEN; + while ((adcp->adcm->ISR & ADC_ISR_ADRDY) == 0) + ; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR |= ADC_CR_ADEN; + while ((adcp->adcs->ISR & ADC_ISR_ADRDY) == 0) + ; +#endif +#endif +} + +/** + * @brief Disables the ADC analog circuit. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_analog_off(ADCDriver *adcp) { + + adcp->adcm->CR |= ADC_CR_ADDIS; + while ((adcp->adcm->CR & ADC_CR_ADDIS) != 0) + ; +#if STM32_ADC_DUAL_MODE + adcp->adcs->CR |= ADC_CR_ADDIS; + while ((adcp->adcs->CR & ADC_CR_ADDIS) != 0) + ; +#endif +} + +/** + * @brief Calibrates and ADC unit. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_calibrate(ADCDriver *adcp) { + +#if defined(STM32F3XX) + osalDbgAssert(adcp->adcm->CR == ADC_CR_ADVREGEN_0, "invalid register state"); + adcp->adcm->CR |= ADC_CR_ADCAL; + while ((adcp->adcm->CR & ADC_CR_ADCAL) != 0) + ; +#if STM32_ADC_DUAL_MODE + osalDbgAssert(adcp->adcs->CR == ADC_CR_ADVREGEN_0, "invalid register state"); + adcp->adcs->CR |= ADC_CR_ADCAL; + while ((adcp->adcs->CR & ADC_CR_ADCAL) != 0) + ; +#endif +#endif + +#if defined(STM32L4XX) + osalDbgAssert(adcp->adcm->CR == ADC_CR_ADVREGEN, "invalid register state"); + adcp->adcm->CR |= ADC_CR_ADCAL; + while ((adcp->adcm->CR & ADC_CR_ADCAL) != 0) + ; +#if STM32_ADC_DUAL_MODE + osalDbgAssert(adcp->adcs->CR == ADC_CR_ADVREGEN, "invalid register state"); + adcp->adcs->CR |= ADC_CR_ADCAL; + while ((adcp->adcs->CR & ADC_CR_ADCAL) != 0) + ; +#endif +#endif +} + +/** + * @brief Stops an ongoing conversion, if any. + * + * @param[in] adcp pointer to the @p ADCDriver object + */ +static void adc_lld_stop_adc(ADCDriver *adcp) { + + if (adcp->adcm->CR & ADC_CR_ADSTART) { + adcp->adcm->CR |= ADC_CR_ADSTP; + while (adcp->adcm->CR & ADC_CR_ADSTP) + ; + } +} + +/** + * @brief ADC DMA ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void adc_lld_serve_dma_interrupt(ADCDriver *adcp, uint32_t flags) { + + /* DMA errors handling.*/ + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + /* DMA, this could help only if the DMA tries to access an unmapped + address space or violates alignment rules.*/ + _adc_isr_error_code(adcp, ADC_ERR_DMAFAILURE); + } + else { + /* It is possible that the conversion group has already be reset by the + ADC error handler, in this case this interrupt is spurious.*/ + if (adcp->grpp != NULL) { + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _adc_isr_full_code(adcp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _adc_isr_half_code(adcp); + } + } + } +} + +/** + * @brief ADC ISR service routine. + * + * @param[in] adcp pointer to the @p ADCDriver object + * @param[in] isr content of the ISR register + */ +static void adc_lld_serve_interrupt(ADCDriver *adcp, uint32_t isr) { + + /* It could be a spurious interrupt caused by overflows after DMA disabling, + just ignore it in this case.*/ + if (adcp->grpp != NULL) { + /* Note, an overflow may occur after the conversion ended before the driver + is able to stop the ADC, this is why the DMA channel is checked too.*/ + if ((isr & ADC_ISR_OVR) && + (dmaStreamGetTransactionSize(adcp->dmastp) > 0)) { + /* ADC overflow condition, this could happen only if the DMA is unable + to read data fast enough.*/ + _adc_isr_error_code(adcp, ADC_ERR_OVERFLOW); + } + if (isr & ADC_ISR_AWD1) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD1); + } + if (isr & ADC_ISR_AWD2) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD2); + } + if (isr & ADC_ISR_AWD3) { + /* Analog watchdog error.*/ + _adc_isr_error_code(adcp, ADC_ERR_AWD3); + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 || defined(__DOXYGEN__) +/** + * @brief ADC1/ADC2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC1_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + +#if STM32_ADC_DUAL_MODE + isr = ADC1->ISR; + isr |= ADC2->ISR; + ADC1->ISR = isr; + ADC2->ISR = isr; + + adc_lld_serve_interrupt(&ADCD1, isr); +#else /* !STM32_ADC_DUAL_MODE */ +#if STM32_ADC_USE_ADC1 + isr = ADC1->ISR; + ADC1->ISR = isr; + + adc_lld_serve_interrupt(&ADCD1, isr); +#endif +#if STM32_ADC_USE_ADC2 + isr = ADC2->ISR; + ADC2->ISR = isr; + + adc_lld_serve_interrupt(&ADCD2, isr); +#endif +#endif /* !STM32_ADC_DUAL_MODE */ + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC3 || defined(__DOXYGEN__) +/** + * @brief ADC3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC3_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADC3->ISR; + ADC3->ISR = isr; + + adc_lld_serve_interrupt(&ADCD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#if STM32_ADC_DUAL_MODE +/** + * @brief ADC4 interrupt handler (as ADC3 slave). + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC4_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADC4->ISR; + ADC4->ISR = isr; + + adc_lld_serve_interrupt(&ADCD3, isr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_ADC_DUAL_MODE */ +#endif /* STM32_ADC_USE_ADC3 */ + +#if STM32_ADC_USE_ADC4 || defined(__DOXYGEN__) +/** + * @brief ADC4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_ADC4_HANDLER) { + uint32_t isr; + + OSAL_IRQ_PROLOGUE(); + + isr = ADC4->ISR; + ADC4->ISR = isr; + + adc_lld_serve_interrupt(&ADCD4, isr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_ADC_USE_ADC4 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ADC driver initialization. + * + * @notapi + */ +void adc_lld_init(void) { + + clkmask = 0; + +#if STM32_ADC_USE_ADC1 + /* Driver initialization.*/ + adcObjectInit(&ADCD1); +#if defined(ADC1_2_COMMON) + ADCD1.adcc = ADC1_2_COMMON; +#elif defined(ADC123_COMMON) + ADCD1.adcc = ADC123_COMMON; +#else + ADCD1.adcc = ADC1_COMMON; +#endif + ADCD1.adcm = ADC1; +#if STM32_ADC_DUAL_MODE + ADCD1.adcs = ADC2; +#endif + ADCD1.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC1_DMA_STREAM); + ADCD1.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC2 + /* Driver initialization.*/ + adcObjectInit(&ADCD2); +#if defined(ADC1_2_COMMON) + ADCD2.adcc = ADC1_2_COMMON; +#elif defined(ADC123_COMMON) + ADCD2.adcc = ADC123_COMMON; +#endif + ADCD2.adcm = ADC2; + ADCD2.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC2_DMA_STREAM); + ADCD2.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC2 */ + +#if STM32_ADC_USE_ADC3 + /* Driver initialization.*/ + adcObjectInit(&ADCD3); +#if defined(ADC3_4_COMMON) + ADCD3.adcc = ADC3_4_COMMON; +#elif defined(ADC123_COMMON) + ADCD1.adcc = ADC123_COMMON; +#else + ADCD3.adcc = ADC3_COMMON; +#endif + ADCD3.adcm = ADC3; +#if STM32_ADC_DUAL_MODE + ADCD3.adcs = ADC4; +#endif + ADCD3.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC3_DMA_STREAM); + ADCD3.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC3 */ + +#if STM32_ADC_USE_ADC4 + /* Driver initialization.*/ + adcObjectInit(&ADCD4); + ADCD4.adcc = ADC3_4_COMMON; + ADCD4.adcm = ADC4; + ADCD4.dmastp = STM32_DMA_STREAM(STM32_ADC_ADC4_DMA_STREAM); + ADCD4.dmamode = ADC_DMA_SIZE | + STM32_DMA_CR_PL(STM32_ADC_ADC4_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; +#endif /* STM32_ADC_USE_ADC4 */ + + /* IRQs setup.*/ +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 + nvicEnableVector(STM32_ADC1_NUMBER, STM32_ADC_ADC12_IRQ_PRIORITY); +#endif +#if STM32_ADC_USE_ADC3 + nvicEnableVector(STM32_ADC3_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); +#if STM32_ADC_DUAL_MODE + nvicEnableVector(STM32_ADC4_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); +#endif +#endif +#if STM32_ADC_USE_ADC4 + nvicEnableVector(STM32_ADC4_NUMBER, STM32_ADC_ADC3_IRQ_PRIORITY); +#endif + + /* ADC units pre-initializations.*/ +#if defined(STM32F3XX) +#if STM32_ADC_USE_ADC1 || STM32_ADC_USE_ADC2 + rccEnableADC12(FALSE); + rccResetADC12(); + ADC1_2_COMMON->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; + rccDisableADC12(FALSE); +#endif +#if STM32_ADC_USE_ADC3 || STM32_ADC_USE_ADC4 + rccEnableADC34(FALSE); + rccResetADC34(); + ADC3_4_COMMON->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; + rccDisableADC34(FALSE); +#endif +#endif + +#if defined(STM32L4XX) + rccEnableADC123(FALSE); + rccResetADC123(); + ADC123_COMMON->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; + rccDisableADC123(FALSE); +#endif +} + +/** + * @brief Configures and activates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start(ADCDriver *adcp) { + + /* Handling the default configuration.*/ + if (adcp->config == NULL) { + adcp->config = &default_config; + } + + /* If in stopped state then enables the ADC and DMA clocks.*/ + if (adcp->state == ADC_STOP) { +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC1_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 0); +#if defined(STM32F3XX) + rccEnableADC12(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC1 */ + +#if STM32_ADC_USE_ADC2 + if (&ADCD2 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC2_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 1); +#if defined(STM32F3XX) + rccEnableADC12(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC2 */ + +#if STM32_ADC_USE_ADC3 + if (&ADCD3 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC3_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 2); +#if defined(STM32F3XX) + rccEnableADC34(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC3 */ + +#if STM32_ADC_USE_ADC4 + if (&ADCD4 == adcp) { + bool b; + b = dmaStreamAllocate(adcp->dmastp, + STM32_ADC_ADC4_DMA_IRQ_PRIORITY, + (stm32_dmaisr_t)adc_lld_serve_dma_interrupt, + (void *)adcp); + osalDbgAssert(!b, "stream already allocated"); + + clkmask |= (1 << 3); +#if defined(STM32F3XX) + rccEnableADC34(FALSE); +#endif +#if defined(STM32L4XX) + rccEnableADC123(FALSE); +#endif + } +#endif /* STM32_ADC_USE_ADC4 */ + + /* Setting DMA peripheral-side pointer.*/ +#if STM32_ADC_DUAL_MODE + dmaStreamSetPeripheral(adcp->dmastp, &adcp->adcc->CDR); +#else + dmaStreamSetPeripheral(adcp->dmastp, &adcp->adcm->DR); +#endif + + /* Differential channels setting.*/ +#if STM32_ADC_DUAL_MODE + adcp->adcm->DIFSEL = adcp->config->difsel; + adcp->adcs->DIFSEL = adcp->config->difsel; +#else + adcp->adcm->DIFSEL = adcp->config->difsel; +#endif + + /* Master ADC calibration.*/ + adc_lld_vreg_on(adcp); + adc_lld_calibrate(adcp); + + /* Master ADC enabled here in order to reduce conversions latencies.*/ + adc_lld_analog_on(adcp); + } +} + +/** + * @brief Deactivates the ADC peripheral. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop(ADCDriver *adcp) { + + /* If in ready state then disables the ADC clock and analog part.*/ + if (adcp->state == ADC_READY) { + + /* Releasing the associated DMA channel.*/ + dmaStreamRelease(adcp->dmastp); + + /* Stopping the ongoing conversion, if any.*/ + adc_lld_stop_adc(adcp); + + /* Disabling ADC analog circuit and regulator.*/ + adc_lld_analog_off(adcp); + adc_lld_vreg_off(adcp); + +#if defined(STM32L4XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC123_CLOCK_MODE | ADC_DMA_MDMA; +#endif + +#if STM32_ADC_USE_ADC1 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 0); + } +#endif + +#if STM32_ADC_USE_ADC2 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC12_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 1); + } +#endif + +#if STM32_ADC_USE_ADC3 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 2); + } +#endif + +#if STM32_ADC_USE_ADC4 + if (&ADCD1 == adcp) { +#if defined(STM32F3XX) + /* Resetting CCR options except default ones.*/ + adcp->adcc->CCR = STM32_ADC_ADC34_CLOCK_MODE | ADC_DMA_MDMA; +#endif + clkmask &= ~(1 << 3); + } +#endif + +#if defined(STM32F3XX) + if ((clkmask & 0x3) == 0) { + rccDisableADC12(FALSE); + } + if ((clkmask & 0xC) == 0) { + rccDisableADC34(FALSE); + } +#endif + +#if defined(STM32L4XX) + if ((clkmask & 0x7) == 0) { + rccDisableADC123(FALSE); + } +#endif + } +} + +/** + * @brief Starts an ADC conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_start_conversion(ADCDriver *adcp) { + uint32_t dmamode, cfgr; + const ADCConversionGroup *grpp = adcp->grpp; +#if STM32_ADC_DUAL_MODE + uint32_t ccr = grpp->ccr & ~(ADC_CCR_CKMODE_MASK | ADC_CCR_MDMA_MASK); +#endif + + osalDbgAssert(!STM32_ADC_DUAL_MODE || ((grpp->num_channels & 1) == 0), + "odd number of channels in dual mode"); + + /* Calculating control registers values.*/ + dmamode = adcp->dmamode; + cfgr = grpp->cfgr | ADC_CFGR_DMAEN; + if (grpp->circular) { + dmamode |= STM32_DMA_CR_CIRC; +#if STM32_ADC_DUAL_MODE + ccr |= ADC_CCR_DMACFG_CIRCULAR; +#else + cfgr |= ADC_CFGR_DMACFG_CIRCULAR; +#endif + if (adcp->depth > 1) { + /* If circular buffer depth > 1, then the half transfer interrupt + is enabled in order to allow streaming processing.*/ + dmamode |= STM32_DMA_CR_HTIE; + } + } + + /* DMA setup.*/ + dmaStreamSetMemory0(adcp->dmastp, adcp->samples); +#if STM32_ADC_DUAL_MODE + dmaStreamSetTransactionSize(adcp->dmastp, ((uint32_t)grpp->num_channels/2) * + (uint32_t)adcp->depth); +#else + dmaStreamSetTransactionSize(adcp->dmastp, (uint32_t)grpp->num_channels * + (uint32_t)adcp->depth); +#endif + dmaStreamSetMode(adcp->dmastp, dmamode); + dmaStreamEnable(adcp->dmastp); + + /* ADC setup, if it is defined a callback for the analog watch dog then it + is enabled.*/ + adcp->adcm->ISR = adcp->adcm->ISR; + adcp->adcm->IER = ADC_IER_OVR | ADC_IER_AWD1; + adcp->adcm->TR1 = grpp->tr1; +#if STM32_ADC_DUAL_MODE + + /* Configuring the CCR register with the user-specified settings + in the conversion group configuration structure, static settings are + preserved.*/ + adcp->adcc->CCR = (adcp->adcc->CCR & + (ADC_CCR_CKMODE_MASK | ADC_CCR_MDMA_MASK)) | ccr; + + adcp->adcm->SMPR1 = grpp->smpr[0]; + adcp->adcm->SMPR2 = grpp->smpr[1]; + adcp->adcm->SQR1 = grpp->sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels / 2); + adcp->adcm->SQR2 = grpp->sqr[1]; + adcp->adcm->SQR3 = grpp->sqr[2]; + adcp->adcm->SQR4 = grpp->sqr[3]; + adcp->adcs->SMPR1 = grpp->ssmpr[0]; + adcp->adcs->SMPR2 = grpp->ssmpr[1]; + adcp->adcs->SQR1 = grpp->ssqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels / 2); + adcp->adcs->SQR2 = grpp->ssqr[1]; + adcp->adcs->SQR3 = grpp->ssqr[2]; + adcp->adcs->SQR4 = grpp->ssqr[3]; + +#else /* !STM32_ADC_DUAL_MODE */ + adcp->adcm->SMPR1 = grpp->smpr[0]; + adcp->adcm->SMPR2 = grpp->smpr[1]; + adcp->adcm->SQR1 = grpp->sqr[0] | ADC_SQR1_NUM_CH(grpp->num_channels); + adcp->adcm->SQR2 = grpp->sqr[1]; + adcp->adcm->SQR3 = grpp->sqr[2]; + adcp->adcm->SQR4 = grpp->sqr[3]; +#endif /* !STM32_ADC_DUAL_MODE */ + + /* ADC configuration.*/ + adcp->adcm->CFGR = cfgr; + + /* Starting conversion.*/ + adcp->adcm->CR |= ADC_CR_ADSTART; +} + +/** + * @brief Stops an ongoing conversion. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adc_lld_stop_conversion(ADCDriver *adcp) { + + dmaStreamDisable(adcp->dmastp); + adc_lld_stop_adc(adcp); +} + +/** + * @brief Enables the VREFEN bit. + * @details The VREFEN bit is required in order to sample the VREF channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32EnableVREF(ADCDriver *adcp) { + + adcp->adcc->CCR |= ADC_CCR_VBATEN; +} + +/** + * @brief Disables the VREFEN bit. + * @details The VREFEN bit is required in order to sample the VREF channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32DisableVREF(ADCDriver *adcp) { + + adcp->adcc->CCR &= ~ADC_CCR_VBATEN; +} + +/** + * @brief Enables the TSEN bit. + * @details The TSEN bit is required in order to sample the internal + * temperature sensor and internal reference voltage. + * @note This is an STM32-only functionality. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32EnableTS(ADCDriver *adcp) { + + adcp->adcc->CCR |= ADC_CCR_TSEN; +} + +/** + * @brief Disables the TSEN bit. + * @details The TSEN bit is required in order to sample the internal + * temperature sensor and internal reference voltage. + * @note This is an STM32-only functionality. + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32DisableTS(ADCDriver *adcp) { + + adcp->adcc->CCR &= ~ADC_CCR_TSEN; +} + +/** + * @brief Enables the VBATEN bit. + * @details The VBATEN bit is required in order to sample the VBAT channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32EnableVBAT(ADCDriver *adcp) { + + adcp->adcc->CCR |= ADC_CCR_VBATEN; +} + +/** + * @brief Disables the VBATEN bit. + * @details The VBATEN bit is required in order to sample the VBAT channel. + * @note This is an STM32-only functionality. + * @note This function is meant to be called after @p adcStart(). + * + * @param[in] adcp pointer to the @p ADCDriver object + * + * @notapi + */ +void adcSTM32DisableVBAT(ADCDriver *adcp) { + + adcp->adcc->CCR &= ~ADC_CCR_VBATEN; +} + +#endif /* HAL_USE_ADC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.h b/os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.h new file mode 100644 index 000000000..8e5a02c15 --- /dev/null +++ b/os/hal/ports/STM32/LLD/ADCv3/hal_adc_lld.h @@ -0,0 +1,888 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32F3xx/adc_lld.h + * @brief STM32F3xx ADC subsystem low level driver header. + * + * @addtogroup ADC + * @{ + */ + +#ifndef _ADC_LLD_H_ +#define _ADC_LLD_H_ + +#if HAL_USE_ADC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Available analog channels + * @{ + */ +#define ADC_CHANNEL_IN0 0 /**< @brief External analog input 0. */ +#define ADC_CHANNEL_IN1 1 /**< @brief External analog input 1. */ +#define ADC_CHANNEL_IN2 2 /**< @brief External analog input 2. */ +#define ADC_CHANNEL_IN3 3 /**< @brief External analog input 3. */ +#define ADC_CHANNEL_IN4 4 /**< @brief External analog input 4. */ +#define ADC_CHANNEL_IN5 5 /**< @brief External analog input 5. */ +#define ADC_CHANNEL_IN6 6 /**< @brief External analog input 6. */ +#define ADC_CHANNEL_IN7 7 /**< @brief External analog input 7. */ +#define ADC_CHANNEL_IN8 8 /**< @brief External analog input 8. */ +#define ADC_CHANNEL_IN9 9 /**< @brief External analog input 9. */ +#define ADC_CHANNEL_IN10 10 /**< @brief External analog input 10. */ +#define ADC_CHANNEL_IN11 11 /**< @brief External analog input 11. */ +#define ADC_CHANNEL_IN12 12 /**< @brief External analog input 12. */ +#define ADC_CHANNEL_IN13 13 /**< @brief External analog input 13. */ +#define ADC_CHANNEL_IN14 14 /**< @brief External analog input 14. */ +#define ADC_CHANNEL_IN15 15 /**< @brief External analog input 15. */ +#define ADC_CHANNEL_IN16 16 /**< @brief External analog input 16. */ +#define ADC_CHANNEL_IN17 17 /**< @brief External analog input 17. */ +#define ADC_CHANNEL_IN18 18 /**< @brief External analog input 18. */ +/** @} */ + +/** + * @name Sampling rates + * @{ + */ +#if defined(STM32F3XX) +#define ADC_SMPR_SMP_1P5 0 /**< @brief 14 cycles conversion time */ +#define ADC_SMPR_SMP_2P5 1 /**< @brief 15 cycles conversion time. */ +#define ADC_SMPR_SMP_4P5 2 /**< @brief 17 cycles conversion time. */ +#define ADC_SMPR_SMP_7P5 3 /**< @brief 20 cycles conversion time. */ +#define ADC_SMPR_SMP_19P5 4 /**< @brief 32 cycles conversion time. */ +#define ADC_SMPR_SMP_61P5 5 /**< @brief 74 cycles conversion time. */ +#define ADC_SMPR_SMP_181P5 6 /**< @brief 194 cycles conversion time. */ +#define ADC_SMPR_SMP_601P5 7 /**< @brief 614 cycles conversion time. */ +#endif +#if defined(STM32L4XX) +#define ADC_SMPR_SMP_2P5 0 /**< @brief 15 cycles conversion time */ +#define ADC_SMPR_SMP_6P5 1 /**< @brief 19 cycles conversion time. */ +#define ADC_SMPR_SMP_12P5 2 /**< @brief 25 cycles conversion time. */ +#define ADC_SMPR_SMP_24P5 3 /**< @brief 37 cycles conversion time. */ +#define ADC_SMPR_SMP_47P5 4 /**< @brief 60 cycles conversion time. */ +#define ADC_SMPR_SMP_92P5 5 /**< @brief 105 cycles conversion time. */ +#define ADC_SMPR_SMP_247P5 6 /**< @brief 260 cycles conversion time. */ +#define ADC_SMPR_SMP_640P5 7 /**< @brief 653 cycles conversion time. */ +#endif +/** @} */ + +/** + * @name Resolution + * @{ + */ +#define ADC_CFGR1_RES_12BIT (0 << 3) +#define ADC_CFGR1_RES_10BIT (1 << 3) +#define ADC_CFGR1_RES_8BIT (2 << 3) +#define ADC_CFGR1_RES_6BIT (3 << 3) +/** @} */ + +/** + * @name CFGR register configuration helpers + * @{ + */ +#define ADC_CFGR_DMACFG_MASK (1 << 1) +#define ADC_CFGR_DMACFG_ONESHOT (0 << 1) +#define ADC_CFGR_DMACFG_CIRCULAR (1 << 1) + +#define ADC_CFGR_RES_MASK (3 << 3) +#define ADC_CFGR_RES_12BITS (0 << 3) +#define ADC_CFGR_RES_10BITS (1 << 3) +#define ADC_CFGR_RES_8BITS (2 << 3) +#define ADC_CFGR_RES_6BITS (3 << 3) + +#define ADC_CFGR_ALIGN_MASK (1 << 5) +#define ADC_CFGR_ALIGN_RIGHT (0 << 5) +#define ADC_CFGR_ALIGN_LEFT (1 << 5) + +#define ADC_CFGR_EXTSEL_MASK (15 << 6) +#define ADC_CFGR_EXTSEL_SRC(n) ((n) << 6) + +#define ADC_CFGR_EXTEN_MASK (3 << 10) +#define ADC_CFGR_EXTEN_DISABLED (0 << 10) +#define ADC_CFGR_EXTEN_RISING (1 << 10) +#define ADC_CFGR_EXTEN_FALLING (2 << 10) +#define ADC_CFGR_EXTEN_BOTH (3 << 10) + +#define ADC_CFGR_DISCEN_MASK (1 << 16) +#define ADC_CFGR_DISCEN_DISABLED (0 << 16) +#define ADC_CFGR_DISCEN_ENABLED (1 << 16) + +#define ADC_CFGR_DISCNUM_MASK (7 << 17) +#define ADC_CFGR_DISCNUM_VAL(n) ((n) << 17) + +#define ADC_CFGR_AWD1_DISABLED 0 +#define ADC_CFGR_AWD1_ALL (1 << 23) +#define ADC_CFGR_AWD1_SINGLE(n) (((n) << 26) | (1 << 23) | (1 << 22)) +/** @} */ + +/** + * @name CCR register configuration helpers + * @{ + */ +#define ADC_CCR_DUAL_MASK (31 << 0) +#define ADC_CCR_DUAL_FIELD(n) ((n) << 0) +#define ADC_CCR_DELAY_MASK (15 << 8) +#define ADC_CCR_DELAY_FIELD(n) ((n) << 8) +#define ADC_CCR_DMACFG_MASK (1 << 13) +#define ADC_CCR_DMACFG_ONESHOT (0 << 13) +#define ADC_CCR_DMACFG_CIRCULAR (1 << 13) +#define ADC_CCR_MDMA_MASK (3 << 14) +#define ADC_CCR_MDMA_DISABLED (0 << 14) +#define ADC_CCR_MDMA_WORD (2 << 14) +#define ADC_CCR_MDMA_HWORD (3 << 14) +#define ADC_CCR_CKMODE_MASK (3 << 16) +#define ADC_CCR_CKMODE_ADCCK (0 << 16) +#define ADC_CCR_CKMODE_AHB_DIV1 (1 << 16) +#define ADC_CCR_CKMODE_AHB_DIV2 (2 << 16) +#define ADC_CCR_CKMODE_AHB_DIV4 (3 << 16) + +/* F3 headers do not define the following macros, L4 headers do.*/ +#if !defined(ADC_CCR_VREFEN) || defined(__DOXYGEN__) +#define ADC_CCR_VREFEN (1 << 22) +#endif + +#if !defined(ADC_CCR_TSEN) || defined(__DOXYGEN__) +#define ADC_CCR_TSEN (1 << 23) +#endif + +#if !defined(ADC_CCR_VBATEN) || defined(__DOXYGEN__) +#define ADC_CCR_VBATEN (1 << 24) +#endif +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Enables the ADC master/slave mode. + * @note In dual mode only ADCD1 and ADCD3 are available. + */ +#if !defined(STM32_ADC_DUAL_MODE) || defined(__DOXYGEN__) +#define STM32_ADC_DUAL_MODE FALSE +#endif + +/** + * @brief Makes the ADC samples type an 8bits one. + * @note 10 and 12 bits sampling mode must not be used when this option + * is enabled. + */ +#if !defined(STM32_ADC_COMPACT_SAMPLES) || defined(__DOXYGEN__) +#define STM32_ADC_COMPACT_SAMPLES FALSE +#endif + +/** + * @brief ADC1 driver enable switch. + * @details If set to @p TRUE the support for ADC1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_ADC_USE_ADC1) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC1 FALSE +#endif + +/** + * @brief ADC2 driver enable switch. + * @details If set to @p TRUE the support for ADC2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_ADC_USE_ADC2) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC2 FALSE +#endif + +/** + * @brief ADC3 driver enable switch. + * @details If set to @p TRUE the support for ADC3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_ADC_USE_ADC3) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC3 FALSE +#endif +/** + * @brief ADC4 driver enable switch. + * @details If set to @p TRUE the support for ADC4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_ADC_USE_ADC4) || defined(__DOXYGEN__) +#define STM32_ADC_USE_ADC4 FALSE +#endif + +/** + * @brief ADC1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC2_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC3 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC3_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC4 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_ADC_ADC4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC4_DMA_PRIORITY 2 +#endif + +/** + * @brief ADC1/ADC2 interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC12_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC12_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC3 interrupt priority level setting. + */ +#if !defined(STM32_ADC3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC3_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC4 interrupt priority level setting. + */ +#if !defined(STM32_ADC4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC4_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC1 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC2 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC2_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC3 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY 5 +#endif + +/** + * @brief ADC4 DMA interrupt priority level setting. + */ +#if !defined(STM32_ADC_ADC4_DMA_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ADC_ADC4_DMA_IRQ_PRIORITY 5 +#endif + +#if defined(STM32F3XX) || defined(__DOXYGEN__) +/** + * @brief ADC1/ADC2 clock source and mode. + */ +#if !defined(STM32_ADC_ADC12_CLOCK_MODE) || defined(__DOXYGEN__) +#define STM32_ADC_ADC12_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 +#endif + +/** + * @brief ADC3/ADC4 clock source and mode. + */ +#if !defined(STM32_ADC_ADC34_CLOCK_MODE) || defined(__DOXYGEN__) +#define STM32_ADC_ADC34_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 +#endif +#endif /* defined(STM32F3XX) */ + +#if defined(STM32L4XX) || defined(__DOXYGEN__) +/** + * @brief ADC1/ADC2/ADC3 clock source and mode. + */ +#if !defined(STM32_ADC_ADC123_CLOCK_MODE) || defined(__DOXYGEN__) +#define STM32_ADC_ADC123_CLOCK_MODE ADC_CCR_CKMODE_AHB_DIV1 +#endif +#endif /* defined(STM32L4XX) */ + +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/* Supported devices checks.*/ +#if !defined(STM32F3XX) && !defined(STM32L4XX) +#error "ADCv3 only supports F3 and L4 STM32 devices" +#endif + +/* Registry checks.*/ +#if !defined(STM32_HAS_ADC1) || !defined(STM32_HAS_ADC2) || \ + !defined(STM32_HAS_ADC3) || !defined(STM32_HAS_ADC4) +#error "STM32_ADC_USE_ADCx not defined in registry" +#endif + +#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_HANDLER)) || \ + (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_HANDLER)) || \ + (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_HANDLER)) || \ + (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_HANDLER)) +#error "STM32_ADCx_HANDLER not defined in registry" +#endif + +#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_NUMBER)) || \ + (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_NUMBER)) || \ + (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_NUMBER)) || \ + (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_NUMBER)) +#error "STM32_ADCx_NUMBER not defined in registry" +#endif + +#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_DMA_MSK)) || \ + (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_DMA_MSK)) || \ + (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_DMA_MSK)) || \ + (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_DMA_MSK)) +#error "STM32_ADCx_DMA_MSK not defined in registry" +#endif + +#if (STM32_ADC_USE_ADC1 && !defined(STM32_ADC1_DMA_CHN)) || \ + (STM32_ADC_USE_ADC2 && !defined(STM32_ADC2_DMA_CHN)) || \ + (STM32_ADC_USE_ADC3 && !defined(STM32_ADC3_DMA_CHN)) || \ + (STM32_ADC_USE_ADC4 && !defined(STM32_ADC4_DMA_CHN)) +#error "STM32_ADCx_DMA_CHN not defined in registry" +#endif + +/* Units checks.*/ +#if STM32_ADC_USE_ADC1 && !STM32_HAS_ADC1 +#error "ADC1 not present in the selected device" +#endif + +#if STM32_ADC_USE_ADC2 && !STM32_HAS_ADC2 +#error "ADC2 not present in the selected device" +#endif + +#if STM32_ADC_USE_ADC3 && !STM32_HAS_ADC3 +#error "ADC3 not present in the selected device" +#endif + +#if STM32_ADC_USE_ADC4 && !STM32_HAS_ADC4 +#error "ADC4 not present in the selected device" +#endif + +/* Units checks related to dual mode.*/ +#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC1 && !STM32_HAS_ADC2 +#error "ADC2 not present in the selected device, required for dual mode" +#endif + +#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC3 && !STM32_HAS_ADC4 +#error "ADC4 not present in the selected device, required for dual mode" +#endif + +#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC2 +#error "ADC2 cannot be used in dual mode" +#endif + +#if STM32_ADC_DUAL_MODE && STM32_ADC_USE_ADC4 +#error "ADC4 cannot be used in dual mode" +#endif + +/* At least one ADC must be assigned.*/ +#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && \ + !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 +#error "ADC driver activated but no ADC peripheral assigned" +#endif + +/* ISR arrangments checks.*/ +#if STM32_ADC1_NUMBER != STM32_ADC2_NUMBER +#error "ADCv3 driver expects STM32_ADC1_NUMBER == STM32_ADC2_NUMBER from registry" +#endif + +/* ADC IRQ priority tests.*/ +#if STM32_ADC_USE_ADC1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC12_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC1" +#endif + +/* ADC IRQ priority tests.*/ +#if STM32_ADC_USE_ADC2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC12_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC2" +#endif + +#if STM32_ADC_USE_ADC3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC3" +#endif + +#if STM32_ADC_USE_ADC4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC4" +#endif + +/* DMA IRQ priority tests.*/ +#if STM32_ADC_USE_ADC1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC1 DMA" +#endif + +#if STM32_ADC_USE_ADC2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC2_DMA_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC2 DMA" +#endif + +#if STM32_ADC_USE_ADC3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC3_DMA_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC3 DMA" +#endif + +#if STM32_ADC_USE_ADC4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ADC_ADC4_DMA_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to ADC4 DMA" +#endif + +/* DMA priority tests.*/ +#if STM32_ADC_USE_ADC1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to ADC1" +#endif + +#if STM32_ADC_USE_ADC2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to ADC2" +#endif + +#if STM32_ADC_USE_ADC3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to ADC3" +#endif + +#if STM32_ADC_USE_ADC4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to ADC4" +#endif + +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_ADC_USE_ADC1 && !defined(STM32_ADC_ADC1_DMA_STREAM) +#error "ADC1 DMA stream not defined" +#endif + +#if STM32_ADC_USE_ADC2 && !defined(STM32_ADC_ADC2_DMA_STREAM) +#error "ADC2 DMA stream not defined" +#endif + +#if STM32_ADC_USE_ADC3 && !defined(STM32_ADC_ADC3_DMA_STREAM) +#error "ADC3 DMA stream not defined" +#endif + +#if STM32_ADC_USE_ADC4 && !defined(STM32_ADC_ADC4_DMA_STREAM) +#error "ADC4 DMA stream not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_ADC_USE_ADC1 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK) +#error "invalid DMA stream associated to ADC1" +#endif + +#if STM32_ADC_USE_ADC2 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC2_DMA_STREAM, STM32_ADC2_DMA_MSK) +#error "invalid DMA stream associated to ADC2" +#endif + +#if STM32_ADC_USE_ADC3 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC3_DMA_STREAM, STM32_ADC3_DMA_MSK) +#error "invalid DMA stream associated to ADC3" +#endif + +#if STM32_ADC_USE_ADC4 && \ + !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC4_DMA_STREAM, STM32_ADC4_DMA_MSK) +#error "invalid DMA stream associated to ADC4" +#endif + +/* ADC clock source checks.*/ +#if defined(STM32F3XX) +#if STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK +#define STM32_ADC12_CLOCK STM32_ADC12CLK +#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1 +#define STM32_ADC12_CLOCK (STM32_HCLK / 1) +#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2 +#define STM32_ADC12_CLOCK (STM32_HCLK / 2) +#elif STM32_ADC_ADC12_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4 +#define STM32_ADC12_CLOCK (STM32_HCLK / 4) +#else +#error "invalid clock mode selected for STM32_ADC_ADC12_CLOCK_MODE" +#endif + +#if STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK +#define STM32_ADC34_CLOCK STM32_ADC34CLK +#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1 +#define STM32_ADC34_CLOCK (STM32_HCLK / 1) +#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2 +#define STM32_ADC34_CLOCK (STM32_HCLK / 2) +#elif STM32_ADC_ADC34_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4 +#define STM32_ADC34_CLOCK (STM32_HCLK / 4) +#else +#error "invalid clock mode selected for STM32_ADC_ADC12_CLOCK_MODE" +#endif + +#if STM32_ADC12_CLOCK > STM32_ADCCLK_MAX +#error "STM32_ADC12_CLOCK exceeding maximum frequency (STM32_ADCCLK_MAX)" +#endif + +#if STM32_ADC34_CLOCK > STM32_ADCCLK_MAX +#error "STM32_ADC34_CLOCK exceeding maximum frequency (STM32_ADCCLK_MAX)" +#endif +#endif /* defined(STM32F3XX) */ + +#if defined(STM32L4XX) +#if STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_ADCCK +#define STM32_ADC123_CLOCK STM32_ADC12CLK +#elif STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV1 +#define STM32_ADC123_CLOCK (STM32_HCLK / 1) +#elif STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV2 +#define STM32_ADC123_CLOCK (STM32_HCLK / 2) +#elif STM32_ADC_ADC123_CLOCK_MODE == ADC_CCR_CKMODE_AHB_DIV4 +#define STM32_ADC123_CLOCK (STM32_HCLK / 4) +#else +#error "invalid clock mode selected for STM32_ADC_ADC123_CLOCK_MODE" +#endif + +#if STM32_ADC123_CLOCK > STM32_ADCCLK_MAX +#error "STM32_ADC123_CLOCK exceeding maximum frequency (STM32_ADCCLK_MAX)" +#endif +#endif /* defined(STM32L4XX) */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ADC sample data type. + */ +#if !STM32_ADC_COMPACT_SAMPLES || defined(__DOXYGEN__) +typedef uint16_t adcsample_t; +#else +typedef uint8_t adcsample_t; +#endif + +/** + * @brief Channels number in a conversion group. + */ +typedef uint16_t adc_channels_num_t; + +/** + * @brief Possible ADC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + ADC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + ADC_ERR_OVERFLOW = 1, /**< ADC overflow condition. */ + ADC_ERR_AWD1 = 2, /**< Watchdog 1 triggered. */ + ADC_ERR_AWD2 = 3, /**< Watchdog 2 triggered. */ + ADC_ERR_AWD3 = 4 /**< Watchdog 3 triggered. */ +} adcerror_t; + +/** + * @brief Type of a structure representing an ADC driver. + */ +typedef struct ADCDriver ADCDriver; + +/** + * @brief ADC notification callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] buffer pointer to the most recent samples data + * @param[in] n number of buffer rows available starting from @p buffer + */ +typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n); + +/** + * @brief ADC error callback type. + * + * @param[in] adcp pointer to the @p ADCDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err); + +/** + * @brief Conversion group configuration structure. + * @details This implementation-dependent structure describes a conversion + * operation. + * @note The use of this configuration structure requires knowledge of + * STM32 ADC cell registers interface, please refer to the STM32 + * reference manual for details. + */ +typedef struct { + /** + * @brief Enables the circular buffer mode for the group. + */ + bool circular; + /** + * @brief Number of the analog channels belonging to the conversion group. + */ + adc_channels_num_t num_channels; + /** + * @brief Callback function associated to the group or @p NULL. + */ + adccallback_t end_cb; + /** + * @brief Error callback or @p NULL. + */ + adcerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief ADC CFGR register initialization data. + * @note The bits DMAEN and DMACFG are enforced internally + * to the driver, keep them to zero. + * @note The bits @p ADC_CFGR_CONT or @p ADC_CFGR_DISCEN must be + * specified in continuous mode or if the buffer depth is + * greater than one. + */ + uint32_t cfgr; + /** + * @brief ADC TR1 register initialization data. + */ + uint32_t tr1; +#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__) + /** + * @brief ADC CCR register initialization data. + * @note The bits CKMODE, MDMA, DMACFG are enforced internally to the + * driver, keep them to zero. + * @note This field is only present in dual mode. + */ + uint32_t ccr; +#endif + /** + * @brief ADC SMPRx registers initialization data. + */ + uint32_t smpr[2]; + /** + * @brief ADC SQRx register initialization data. + */ + uint32_t sqr[4]; +#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__) + /** + * @brief Slave ADC SMPRx registers initialization data. + * @note This field is only present in dual mode. + */ + uint32_t ssmpr[2]; + /** + * @brief Slave ADC SQRx register initialization data. + * @note This field is only present in dual mode. + */ + uint32_t ssqr[4]; +#endif /* STM32_ADC_DUAL_MODE */ +} ADCConversionGroup; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief ADC DIFSEL register initialization data. + */ + uint32_t difsel; +} ADCConfig; + +/** + * @brief Structure representing an ADC driver. + */ +struct ADCDriver { + /** + * @brief Driver state. + */ + adcstate_t state; + /** + * @brief Current configuration data. + */ + const ADCConfig *config; + /** + * @brief Current samples buffer pointer or @p NULL. + */ + adcsample_t *samples; + /** + * @brief Current samples buffer depth or @p 0. + */ + size_t depth; + /** + * @brief Current conversion group pointer or @p NULL. + */ + const ADCConversionGroup *grpp; +#if ADC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif +#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* ADC_USE_MUTUAL_EXCLUSION */ +#if defined(ADC_DRIVER_EXT_FIELDS) + ADC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the master ADCx registers block. + */ + ADC_TypeDef *adcm; +#if STM32_ADC_DUAL_MODE || defined(__DOXYGEN__) + /** + * @brief Pointer to the slave ADCx registers block. + */ + ADC_TypeDef *adcs; +#endif /* STM32_ADC_DUAL_MODE */ + /** + * @brief Pointer to the common ADCx_y registers block. + */ + ADC_Common_TypeDef *adcc; + /** + * @brief Pointer to associated DMA channel. + */ + const stm32_dma_stream_t *dmastp; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @name Threashold register initializer + * @{ + */ +#define ADC_TR(low, high) (((uint32_t)(high) << 16) | (uint32_t)(low)) +/** @} */ + +/** + * @name Sequences building helper macros + * @{ + */ +/** + * @brief Number of channels in a conversion sequence. + */ +#define ADC_SQR1_NUM_CH(n) (((n) - 1) << 0) + +#define ADC_SQR1_SQ1_N(n) ((n) << 6) /**< @brief 1st channel in seq. */ +#define ADC_SQR1_SQ2_N(n) ((n) << 12) /**< @brief 2nd channel in seq. */ +#define ADC_SQR1_SQ3_N(n) ((n) << 18) /**< @brief 3rd channel in seq. */ +#define ADC_SQR1_SQ4_N(n) ((n) << 24) /**< @brief 4th channel in seq. */ + +#define ADC_SQR2_SQ5_N(n) ((n) << 0) /**< @brief 5th channel in seq. */ +#define ADC_SQR2_SQ6_N(n) ((n) << 6) /**< @brief 6th channel in seq. */ +#define ADC_SQR2_SQ7_N(n) ((n) << 12) /**< @brief 7th channel in seq. */ +#define ADC_SQR2_SQ8_N(n) ((n) << 18) /**< @brief 8th channel in seq. */ +#define ADC_SQR2_SQ9_N(n) ((n) << 24) /**< @brief 9th channel in seq. */ + +#define ADC_SQR3_SQ10_N(n) ((n) << 0) /**< @brief 10th channel in seq.*/ +#define ADC_SQR3_SQ11_N(n) ((n) << 6) /**< @brief 11th channel in seq.*/ +#define ADC_SQR3_SQ12_N(n) ((n) << 12) /**< @brief 12th channel in seq.*/ +#define ADC_SQR3_SQ13_N(n) ((n) << 18) /**< @brief 13th channel in seq.*/ +#define ADC_SQR3_SQ14_N(n) ((n) << 24) /**< @brief 14th channel in seq.*/ + +#define ADC_SQR4_SQ15_N(n) ((n) << 0) /**< @brief 15th channel in seq.*/ +#define ADC_SQR4_SQ16_N(n) ((n) << 6) /**< @brief 16th channel in seq.*/ +/** @} */ + +/** + * @name Sampling rate settings helper macros + * @{ + */ +#define ADC_SMPR1_SMP_AN0(n) ((n) << 0) /**< @brief AN0 sampling time. */ +#define ADC_SMPR1_SMP_AN1(n) ((n) << 3) /**< @brief AN1 sampling time. */ +#define ADC_SMPR1_SMP_AN2(n) ((n) << 6) /**< @brief AN2 sampling time. */ +#define ADC_SMPR1_SMP_AN3(n) ((n) << 9) /**< @brief AN3 sampling time. */ +#define ADC_SMPR1_SMP_AN4(n) ((n) << 12) /**< @brief AN4 sampling time. */ +#define ADC_SMPR1_SMP_AN5(n) ((n) << 15) /**< @brief AN5 sampling time. */ +#define ADC_SMPR1_SMP_AN6(n) ((n) << 18) /**< @brief AN6 sampling time. */ +#define ADC_SMPR1_SMP_AN7(n) ((n) << 21) /**< @brief AN7 sampling time. */ +#define ADC_SMPR1_SMP_AN8(n) ((n) << 24) /**< @brief AN8 sampling time. */ +#define ADC_SMPR1_SMP_AN9(n) ((n) << 27) /**< @brief AN9 sampling time. */ + +#define ADC_SMPR2_SMP_AN10(n) ((n) << 0) /**< @brief AN10 sampling time. */ +#define ADC_SMPR2_SMP_AN11(n) ((n) << 3) /**< @brief AN11 sampling time. */ +#define ADC_SMPR2_SMP_AN12(n) ((n) << 6) /**< @brief AN12 sampling time. */ +#define ADC_SMPR2_SMP_AN13(n) ((n) << 9) /**< @brief AN13 sampling time. */ +#define ADC_SMPR2_SMP_AN14(n) ((n) << 12) /**< @brief AN14 sampling time. */ +#define ADC_SMPR2_SMP_AN15(n) ((n) << 15) /**< @brief AN15 sampling time. */ +#define ADC_SMPR2_SMP_AN16(n) ((n) << 18) /**< @brief AN16 sampling time. */ +#define ADC_SMPR2_SMP_AN17(n) ((n) << 21) /**< @brief AN17 sampling time. */ +#define ADC_SMPR2_SMP_AN18(n) ((n) << 24) /**< @brief AN18 sampling time. */ +/** @} */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_ADC_USE_ADC1 && !defined(__DOXYGEN__) +extern ADCDriver ADCD1; +#endif + +#if STM32_ADC_USE_ADC2 && !defined(__DOXYGEN__) +extern ADCDriver ADCD2; +#endif + +#if STM32_ADC_USE_ADC3 && !defined(__DOXYGEN__) +extern ADCDriver ADCD3; +#endif + +#if STM32_ADC_USE_ADC4 && !defined(__DOXYGEN__) +extern ADCDriver ADCD4; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void adc_lld_init(void); + void adc_lld_start(ADCDriver *adcp); + void adc_lld_stop(ADCDriver *adcp); + void adc_lld_start_conversion(ADCDriver *adcp); + void adc_lld_stop_conversion(ADCDriver *adcp); + void adcSTM32EnableVREF(ADCDriver *adcp); + void adcSTM32DisableVREF(ADCDriver *adcp); + void adcSTM32EnableTS(ADCDriver *adcp); + void adcSTM32DisableTS(ADCDriver *adcp); + void adcSTM32EnableVBAT(ADCDriver *adcp); + void adcSTM32DisableVBAT(ADCDriver *adcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ADC */ + +#endif /* _ADC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/CANv1/can_lld.c b/os/hal/ports/STM32/LLD/CANv1/can_lld.c deleted file mode 100644 index 9f68fdc25..000000000 --- a/os/hal/ports/STM32/LLD/CANv1/can_lld.c +++ /dev/null @@ -1,847 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/can_lld.c - * @brief STM32 CAN subsystem low level driver source. - * - * @addtogroup CAN - * @{ - */ - -#include "hal.h" - -#if HAL_USE_CAN || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/* - * Addressing differences in the headers, they seem unable to agree on names. - */ -#if STM32_CAN_USE_CAN1 -#if !defined(CAN1) -#define CAN1 CAN -#endif -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief CAN1 driver identifier.*/ -#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__) -CANDriver CAND1; -#endif - -/** @brief CAN2 driver identifier.*/ -#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__) -CANDriver CAND2; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Programs the filters. - * - * @param[in] can2sb number of the first filter assigned to CAN2 - * @param[in] num number of entries in the filters array, if zero then - * a default filter is programmed - * @param[in] cfp pointer to the filters array, can be @p NULL if - * (num == 0) - * - * @notapi - */ -static void can_lld_set_filters(uint32_t can2sb, - uint32_t num, - const CANFilter *cfp) { - - /* Temporarily enabling CAN1 clock.*/ - rccEnableCAN1(FALSE); - - /* Filters initialization.*/ - CAN1->FMR = (CAN1->FMR & 0xFFFF0000) | (can2sb << 8) | CAN_FMR_FINIT; - if (num > 0) { - uint32_t i, fmask; - - /* All filters cleared.*/ - CAN1->FA1R = 0; - CAN1->FM1R = 0; - CAN1->FS1R = 0; - CAN1->FFA1R = 0; - for (i = 0; i < STM32_CAN_MAX_FILTERS; i++) { - CAN1->sFilterRegister[i].FR1 = 0; - CAN1->sFilterRegister[i].FR2 = 0; - } - - /* Scanning the filters array.*/ - for (i = 0; i < num; i++) { - fmask = 1 << cfp->filter; - if (cfp->mode) - CAN1->FM1R |= fmask; - if (cfp->scale) - CAN1->FS1R |= fmask; - if (cfp->assignment) - CAN1->FFA1R |= fmask; - CAN1->sFilterRegister[cfp->filter].FR1 = cfp->register1; - CAN1->sFilterRegister[cfp->filter].FR2 = cfp->register2; - CAN1->FA1R |= fmask; - cfp++; - } - } - else { - /* Setting up a single default filter that enables everything for both - CANs.*/ - CAN1->sFilterRegister[0].FR1 = 0; - CAN1->sFilterRegister[0].FR2 = 0; -#if STM32_HAS_CAN2 - CAN1->sFilterRegister[can2sb].FR1 = 0; - CAN1->sFilterRegister[can2sb].FR2 = 0; -#endif - CAN1->FM1R = 0; - CAN1->FFA1R = 0; -#if STM32_HAS_CAN2 - CAN1->FS1R = 1 | (1 << can2sb); - CAN1->FA1R = 1 | (1 << can2sb); -#else - CAN1->FS1R = 1; - CAN1->FA1R = 1; -#endif - } - CAN1->FMR &= ~CAN_FMR_FINIT; - - /* Clock disabled, it will be enabled again in can_lld_start().*/ - rccDisableCAN1(FALSE); -} - -/** - * @brief Common TX ISR handler. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -static void can_lld_tx_handler(CANDriver *canp) { - uint32_t tsr; - eventflags_t flags; - - /* Clearing IRQ sources.*/ - tsr = canp->can->TSR; - canp->can->TSR = tsr; - - /* Flags to be signaled through the TX event source.*/ - flags = 0U; - - /* Checking mailbox 0.*/ - if ((tsr & CAN_TSR_RQCP0) != 0U) { - if ((tsr & (CAN_TSR_ALST0 | CAN_TSR_TERR0)) != 0U) { - flags |= CAN_MAILBOX_TO_MASK(1U) << 16U; - } - else { - flags |= CAN_MAILBOX_TO_MASK(1U); - } - } - - /* Checking mailbox 1.*/ - if ((tsr & CAN_TSR_RQCP1) != 0U) { - if ((tsr & (CAN_TSR_ALST1 | CAN_TSR_TERR1)) != 0U) { - flags |= CAN_MAILBOX_TO_MASK(2U) << 16U; - } - else { - flags |= CAN_MAILBOX_TO_MASK(2U); - } - } - - /* Checking mailbox 2.*/ - if ((tsr & CAN_TSR_RQCP2) != 0U) { - if ((tsr & (CAN_TSR_ALST2 | CAN_TSR_TERR2)) != 0U) { - flags |= CAN_MAILBOX_TO_MASK(3U) << 16U; - } - else { - flags |= CAN_MAILBOX_TO_MASK(3U); - } - } - - /* Signaling flags and waking up threads waiting for a transmission slot.*/ - osalSysLockFromISR(); - osalThreadDequeueAllI(&canp->txqueue, MSG_OK); - osalEventBroadcastFlagsI(&canp->txempty_event, flags); - osalSysUnlockFromISR(); -} - -/** - * @brief Common RX0 ISR handler. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -static void can_lld_rx0_handler(CANDriver *canp) { - uint32_t rf0r; - - rf0r = canp->can->RF0R; - if ((rf0r & CAN_RF0R_FMP0) > 0) { - /* No more receive events until the queue 0 has been emptied.*/ - canp->can->IER &= ~CAN_IER_FMPIE0; - osalSysLockFromISR(); - osalThreadDequeueAllI(&canp->rxqueue, MSG_OK); - osalEventBroadcastFlagsI(&canp->rxfull_event, CAN_MAILBOX_TO_MASK(1U)); - osalSysUnlockFromISR(); - } - if ((rf0r & CAN_RF0R_FOVR0) > 0) { - /* Overflow events handling.*/ - canp->can->RF0R = CAN_RF0R_FOVR0; - osalSysLockFromISR(); - osalEventBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR); - osalSysUnlockFromISR(); - } -} - -/** - * @brief Common RX1 ISR handler. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -static void can_lld_rx1_handler(CANDriver *canp) { - uint32_t rf1r; - - rf1r = canp->can->RF1R; - if ((rf1r & CAN_RF1R_FMP1) > 0) { - /* No more receive events until the queue 0 has been emptied.*/ - canp->can->IER &= ~CAN_IER_FMPIE1; - osalSysLockFromISR(); - osalThreadDequeueAllI(&canp->rxqueue, MSG_OK); - osalEventBroadcastFlagsI(&canp->rxfull_event, CAN_MAILBOX_TO_MASK(2U)); - osalSysUnlockFromISR(); - } - if ((rf1r & CAN_RF1R_FOVR1) > 0) { - /* Overflow events handling.*/ - canp->can->RF1R = CAN_RF1R_FOVR1; - osalSysLockFromISR(); - osalEventBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR); - osalSysUnlockFromISR(); - } -} - -/** - * @brief Common SCE ISR handler. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -static void can_lld_sce_handler(CANDriver *canp) { - uint32_t msr; - - /* Clearing IRQ sources.*/ - msr = canp->can->MSR; - canp->can->MSR = msr; - - /* Wakeup event.*/ -#if CAN_USE_SLEEP_MODE - if (msr & CAN_MSR_WKUI) { - canp->state = CAN_READY; - canp->can->MCR &= ~CAN_MCR_SLEEP; - osalSysLockFromISR(); - osalEventBroadcastFlagsI(&canp->wakeup_event, 0); - osalSysUnlockFromISR(); - } -#endif /* CAN_USE_SLEEP_MODE */ - /* Error event.*/ - if (msr & CAN_MSR_ERRI) { - eventflags_t flags; - uint32_t esr = canp->can->ESR; - -#if STM32_CAN_REPORT_ALL_ERRORS - flags = (eventflags_t)(esr & 7); - if ((esr & CAN_ESR_LEC) > 0) - flags |= CAN_FRAMING_ERROR; -#else - flags = 0; -#endif - - osalSysLockFromISR(); - /* The content of the ESR register is copied unchanged in the upper - half word of the listener flags mask.*/ - osalEventBroadcastFlagsI(&canp->error_event, - flags | (eventflags_t)(esr << 16U)); - osalSysUnlockFromISR(); - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__) -#if defined(STM32_CAN1_UNIFIED_HANDLER) -/** - * @brief CAN1 unified interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN1_UNIFIED_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_tx_handler(&CAND1); - can_lld_rx0_handler(&CAND1); - can_lld_rx1_handler(&CAND1); - can_lld_sce_handler(&CAND1); - - OSAL_IRQ_EPILOGUE(); -} -#else /* !defined(STM32_CAN1_UNIFIED_HANDLER) */ - -#if !defined(STM32_CAN1_TX_HANDLER) -#error "STM32_CAN1_TX_HANDLER not defined" -#endif -#if !defined(STM32_CAN1_RX0_HANDLER) -#error "STM32_CAN1_RX0_HANDLER not defined" -#endif -#if !defined(STM32_CAN1_RX1_HANDLER) -#error "STM32_CAN1_RX1_HANDLER not defined" -#endif -#if !defined(STM32_CAN1_SCE_HANDLER) -#error "STM32_CAN1_SCE_HANDLER not defined" -#endif - -/** - * @brief CAN1 TX interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN1_TX_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_tx_handler(&CAND1); - - OSAL_IRQ_EPILOGUE(); -} - -/* - * @brief CAN1 RX0 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN1_RX0_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_rx0_handler(&CAND1); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief CAN1 RX1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN1_RX1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_rx1_handler(&CAND1); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief CAN1 SCE interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN1_SCE_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_sce_handler(&CAND1); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_CAN1_UNIFIED_HANDLER) */ -#endif /* STM32_CAN_USE_CAN1 */ - -#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__) -#if defined(STM32_CAN2_UNIFIED_HANDLER) -/** - * @brief CAN1 unified interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN2_UNIFIED_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_tx_handler(&CAND2); - can_lld_rx0_handler(&CAND2); - can_lld_rx1_handler(&CAND2); - can_lld_sce_handler(&CAND2); - - OSAL_IRQ_EPILOGUE(); -} -#else /* !defined(STM32_CAN2_UNIFIED_HANDLER) */ - -#if !defined(STM32_CAN1_TX_HANDLER) -#error "STM32_CAN1_TX_HANDLER not defined" -#endif -#if !defined(STM32_CAN1_RX0_HANDLER) -#error "STM32_CAN1_RX0_HANDLER not defined" -#endif -#if !defined(STM32_CAN1_RX1_HANDLER) -#error "STM32_CAN1_RX1_HANDLER not defined" -#endif -#if !defined(STM32_CAN1_SCE_HANDLER) -#error "STM32_CAN1_SCE_HANDLER not defined" -#endif - -/** - * @brief CAN2 TX interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN2_TX_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_tx_handler(&CAND2); - - OSAL_IRQ_EPILOGUE(); -} - -/* - * @brief CAN2 RX0 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN2_RX0_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_rx0_handler(&CAND2); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief CAN2 RX1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN2_RX1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_rx1_handler(&CAND2); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief CAN2 SCE interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_CAN2_SCE_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - can_lld_sce_handler(&CAND2); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_CAN2_UNIFIED_HANDLER) */ -#endif /* STM32_CAN_USE_CAN2 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level CAN driver initialization. - * - * @notapi - */ -void can_lld_init(void) { - -#if STM32_CAN_USE_CAN1 - /* Driver initialization.*/ - canObjectInit(&CAND1); - CAND1.can = CAN1; -#endif -#if STM32_CAN_USE_CAN2 - /* Driver initialization.*/ - canObjectInit(&CAND2); - CAND2.can = CAN2; -#endif - - /* Filters initialization.*/ -#if STM32_HAS_CAN2 - can_lld_set_filters(STM32_CAN_MAX_FILTERS / 2, 0, NULL); -#else - can_lld_set_filters(STM32_CAN_MAX_FILTERS, 0, NULL); -#endif -} - -/** - * @brief Configures and activates the CAN peripheral. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -void can_lld_start(CANDriver *canp) { - - /* Clock activation.*/ -#if STM32_CAN_USE_CAN1 - if (&CAND1 == canp) { -#if defined(STM32_CAN1_UNIFIED_NUMBER) - nvicEnableVector(STM32_CAN1_UNIFIED_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); -#else - nvicEnableVector(STM32_CAN1_TX_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); - nvicEnableVector(STM32_CAN1_RX0_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); - nvicEnableVector(STM32_CAN1_RX1_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); - nvicEnableVector(STM32_CAN1_SCE_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); -#endif - rccEnableCAN1(FALSE); - } -#endif -#if STM32_CAN_USE_CAN2 - if (&CAND2 == canp) { - - osalDbgAssert(CAND1.state != CAN_STOP, "CAN1 must be started"); - -#if defined(STM32_CAN2_UNIFIED_NUMBER) - nvicEnableVector(STM32_CAN2_UNIFIED_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); -#else - nvicEnableVector(STM32_CAN2_TX_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); - nvicEnableVector(STM32_CAN2_RX0_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); - nvicEnableVector(STM32_CAN2_RX1_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); - nvicEnableVector(STM32_CAN2_SCE_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); -#endif - rccEnableCAN2(FALSE); - } -#endif - - /* Configuring CAN. */ - canp->can->MCR = CAN_MCR_INRQ; - while ((canp->can->MSR & CAN_MSR_INAK) == 0) - osalThreadSleepS(1); - canp->can->BTR = canp->config->btr; - canp->can->MCR = canp->config->mcr; - - /* Interrupt sources initialization.*/ -#if STM32_CAN_REPORT_ALL_ERRORS - canp->can->IER = CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | - CAN_IER_WKUIE | CAN_IER_ERRIE | CAN_IER_LECIE | - CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE | - CAN_IER_FOVIE0 | CAN_IER_FOVIE1; -#else - canp->can->IER = CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | - CAN_IER_WKUIE | CAN_IER_ERRIE | - CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE | - CAN_IER_FOVIE0 | CAN_IER_FOVIE1; -#endif -} - -/** - * @brief Deactivates the CAN peripheral. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -void can_lld_stop(CANDriver *canp) { - - /* If in ready state then disables the CAN peripheral.*/ - if (canp->state == CAN_READY) { -#if STM32_CAN_USE_CAN1 - if (&CAND1 == canp) { - -#if STM32_CAN_USE_CAN2 - osalDbgAssert(CAND2.state == CAN_STOP, "CAN2 must be stopped"); -#endif - - CAN1->MCR = 0x00010002; /* Register reset value. */ - CAN1->IER = 0x00000000; /* All sources disabled. */ -#if defined(STM32_CAN1_UNIFIED_NUMBER) - nvicDisableVector(STM32_CAN1_UNIFIED_NUMBER); -#else - nvicDisableVector(STM32_CAN1_TX_NUMBER); - nvicDisableVector(STM32_CAN1_RX0_NUMBER); - nvicDisableVector(STM32_CAN1_RX1_NUMBER); - nvicDisableVector(STM32_CAN1_SCE_NUMBER); -#endif - rccDisableCAN1(FALSE); - } -#endif -#if STM32_CAN_USE_CAN2 - if (&CAND2 == canp) { - CAN2->MCR = 0x00010002; /* Register reset value. */ - CAN2->IER = 0x00000000; /* All sources disabled. */ -#if defined(STM32_CAN2_UNIFIED_NUMBER) - nvicDisableVector(STM32_CAN2_UNIFIED_NUMBER); -#else - nvicDisableVector(STM32_CAN2_TX_NUMBER); - nvicDisableVector(STM32_CAN2_RX0_NUMBER); - nvicDisableVector(STM32_CAN2_RX1_NUMBER); - nvicDisableVector(STM32_CAN2_SCE_NUMBER); -#endif - rccDisableCAN2(FALSE); - } -#endif - } -} - -/** - * @brief Determines whether a frame can be transmitted. - * - * @param[in] canp pointer to the @p CANDriver object - * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox - * - * @return The queue space availability. - * @retval FALSE no space in the transmit queue. - * @retval TRUE transmit slot available. - * - * @notapi - */ -bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox) { - - switch (mailbox) { - case CAN_ANY_MAILBOX: - return (canp->can->TSR & CAN_TSR_TME) != 0; - case 1: - return (canp->can->TSR & CAN_TSR_TME0) != 0; - case 2: - return (canp->can->TSR & CAN_TSR_TME1) != 0; - case 3: - return (canp->can->TSR & CAN_TSR_TME2) != 0; - default: - return FALSE; - } -} - -/** - * @brief Inserts a frame into the transmit queue. - * - * @param[in] canp pointer to the @p CANDriver object - * @param[in] ctfp pointer to the CAN frame to be transmitted - * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox - * - * @notapi - */ -void can_lld_transmit(CANDriver *canp, - canmbx_t mailbox, - const CANTxFrame *ctfp) { - uint32_t tir; - CAN_TxMailBox_TypeDef *tmbp; - - /* Pointer to a free transmission mailbox.*/ - switch (mailbox) { - case CAN_ANY_MAILBOX: - tmbp = &canp->can->sTxMailBox[(canp->can->TSR & CAN_TSR_CODE) >> 24]; - break; - case 1: - tmbp = &canp->can->sTxMailBox[0]; - break; - case 2: - tmbp = &canp->can->sTxMailBox[1]; - break; - case 3: - tmbp = &canp->can->sTxMailBox[2]; - break; - default: - return; - } - - /* Preparing the message.*/ - if (ctfp->IDE) - tir = ((uint32_t)ctfp->EID << 3) | ((uint32_t)ctfp->RTR << 1) | - CAN_TI0R_IDE; - else - tir = ((uint32_t)ctfp->SID << 21) | ((uint32_t)ctfp->RTR << 1); - tmbp->TDTR = ctfp->DLC; - tmbp->TDLR = ctfp->data32[0]; - tmbp->TDHR = ctfp->data32[1]; - tmbp->TIR = tir | CAN_TI0R_TXRQ; -} - -/** - * @brief Determines whether a frame has been received. - * - * @param[in] canp pointer to the @p CANDriver object - * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox - * - * @return The queue space availability. - * @retval FALSE no space in the transmit queue. - * @retval TRUE transmit slot available. - * - * @notapi - */ -bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox) { - - switch (mailbox) { - case CAN_ANY_MAILBOX: - return ((canp->can->RF0R & CAN_RF0R_FMP0) != 0 || - (canp->can->RF1R & CAN_RF1R_FMP1) != 0); - case 1: - return (canp->can->RF0R & CAN_RF0R_FMP0) != 0; - case 2: - return (canp->can->RF1R & CAN_RF1R_FMP1) != 0; - default: - return FALSE; - } -} - -/** - * @brief Receives a frame from the input queue. - * - * @param[in] canp pointer to the @p CANDriver object - * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox - * @param[out] crfp pointer to the buffer where the CAN frame is copied - * - * @notapi - */ -void can_lld_receive(CANDriver *canp, - canmbx_t mailbox, - CANRxFrame *crfp) { - uint32_t rir, rdtr; - - if (mailbox == CAN_ANY_MAILBOX) { - if ((canp->can->RF0R & CAN_RF0R_FMP0) != 0) - mailbox = 1; - else if ((canp->can->RF1R & CAN_RF1R_FMP1) != 0) - mailbox = 2; - else { - /* Should not happen, do nothing.*/ - return; - } - } - switch (mailbox) { - case 1: - /* Fetches the message.*/ - rir = canp->can->sFIFOMailBox[0].RIR; - rdtr = canp->can->sFIFOMailBox[0].RDTR; - crfp->data32[0] = canp->can->sFIFOMailBox[0].RDLR; - crfp->data32[1] = canp->can->sFIFOMailBox[0].RDHR; - - /* Releases the mailbox.*/ - canp->can->RF0R = CAN_RF0R_RFOM0; - - /* If the queue is empty re-enables the interrupt in order to generate - events again.*/ - if ((canp->can->RF0R & CAN_RF0R_FMP0) == 0) - canp->can->IER |= CAN_IER_FMPIE0; - break; - case 2: - /* Fetches the message.*/ - rir = canp->can->sFIFOMailBox[1].RIR; - rdtr = canp->can->sFIFOMailBox[1].RDTR; - crfp->data32[0] = canp->can->sFIFOMailBox[1].RDLR; - crfp->data32[1] = canp->can->sFIFOMailBox[1].RDHR; - - /* Releases the mailbox.*/ - canp->can->RF1R = CAN_RF1R_RFOM1; - - /* If the queue is empty re-enables the interrupt in order to generate - events again.*/ - if ((canp->can->RF1R & CAN_RF1R_FMP1) == 0) - canp->can->IER |= CAN_IER_FMPIE1; - break; - default: - /* Should not happen, do nothing.*/ - return; - } - - /* Decodes the various fields in the RX frame.*/ - crfp->RTR = (rir & CAN_RI0R_RTR) >> 1; - crfp->IDE = (rir & CAN_RI0R_IDE) >> 2; - if (crfp->IDE) - crfp->EID = rir >> 3; - else - crfp->SID = rir >> 21; - crfp->DLC = rdtr & CAN_RDT0R_DLC; - crfp->FMI = (uint8_t)(rdtr >> 8); - crfp->TIME = (uint16_t)(rdtr >> 16); -} - -#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__) -/** - * @brief Enters the sleep mode. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -void can_lld_sleep(CANDriver *canp) { - - canp->can->MCR |= CAN_MCR_SLEEP; -} - -/** - * @brief Enforces leaving the sleep mode. - * - * @param[in] canp pointer to the @p CANDriver object - * - * @notapi - */ -void can_lld_wakeup(CANDriver *canp) { - - canp->can->MCR &= ~CAN_MCR_SLEEP; -} -#endif /* CAN_USE_SLEEP_MODE */ - -/** - * @brief Programs the filters. - * @note This is an STM32-specific API. - * - * @param[in] can2sb number of the first filter assigned to CAN2 - * @param[in] num number of entries in the filters array, if zero then - * a default filter is programmed - * @param[in] cfp pointer to the filters array, can be @p NULL if - * (num == 0) - * - * @api - */ -void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp) { - - osalDbgCheck((can2sb >= 1) && (can2sb < STM32_CAN_MAX_FILTERS) && - (num <= STM32_CAN_MAX_FILTERS)); - -#if STM32_CAN_USE_CAN1 - osalDbgAssert(CAND1.state == CAN_STOP, "invalid state"); -#endif -#if STM32_CAN_USE_CAN2 - osalDbgAssert(CAND2.state == CAN_STOP, "invalid state"); -#endif - - can_lld_set_filters(can2sb, num, cfp); -} - -#endif /* HAL_USE_CAN */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/CANv1/can_lld.h b/os/hal/ports/STM32/LLD/CANv1/can_lld.h deleted file mode 100644 index 824c1a66d..000000000 --- a/os/hal/ports/STM32/LLD/CANv1/can_lld.h +++ /dev/null @@ -1,377 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/can_lld.h - * @brief STM32 CAN subsystem low level driver header. - * - * @addtogroup CAN - * @{ - */ - -#ifndef _CAN_LLD_H_ -#define _CAN_LLD_H_ - -#if HAL_USE_CAN || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/* - * The following macros from the ST header file are replaced with better - * equivalents. - */ -#undef CAN_BTR_BRP -#undef CAN_BTR_TS1 -#undef CAN_BTR_TS2 -#undef CAN_BTR_SJW - -/** - * @brief This switch defines whether the driver implementation supports - * a low power switch mode with automatic an wakeup feature. - */ -#define CAN_SUPPORTS_SLEEP TRUE - -/** - * @brief This implementation supports three transmit mailboxes. - */ -#define CAN_TX_MAILBOXES 3 - -/** - * @brief This implementation supports two receive mailboxes. - */ -#define CAN_RX_MAILBOXES 2 - -/** - * @name CAN registers helper macros - * @{ - */ -#define CAN_BTR_BRP(n) (n) /**< @brief BRP field macro.*/ -#define CAN_BTR_TS1(n) ((n) << 16) /**< @brief TS1 field macro.*/ -#define CAN_BTR_TS2(n) ((n) << 20) /**< @brief TS2 field macro.*/ -#define CAN_BTR_SJW(n) ((n) << 24) /**< @brief SJW field macro.*/ - -#define CAN_IDE_STD 0 /**< @brief Standard id. */ -#define CAN_IDE_EXT 1 /**< @brief Extended id. */ - -#define CAN_RTR_DATA 0 /**< @brief Data frame. */ -#define CAN_RTR_REMOTE 1 /**< @brief Remote frame. */ -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief CAN pedantic errors report. - * @details Use of this option is IRQ-intensive. - */ -#if !defined(STM32_CAN_REPORT_ALL_ERRORS) || defined(__DOXYGEN__) -#define STM32_CAN_REPORT_ALL_ERRORS FALSE -#endif - -/** - * @brief CAN1 driver enable switch. - * @details If set to @p TRUE the support for CAN1 is included. - */ -#if !defined(STM32_CAN_USE_CAN1) || defined(__DOXYGEN__) -#define STM32_CAN_USE_CAN1 FALSE -#endif - -/** - * @brief CAN2 driver enable switch. - * @details If set to @p TRUE the support for CAN2 is included. - */ -#if !defined(STM32_CAN_USE_CAN2) || defined(__DOXYGEN__) -#define STM32_CAN_USE_CAN2 FALSE -#endif - -/** - * @brief CAN1 interrupt priority level setting. - */ -#if !defined(STM32_CAN_CAN1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_CAN_CAN1_IRQ_PRIORITY 11 -#endif -/** @} */ - -/** - * @brief CAN2 interrupt priority level setting. - */ -#if !defined(STM32_CAN_CAN2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_CAN_CAN2_IRQ_PRIORITY 11 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_CAN_USE_CAN1 && !STM32_HAS_CAN1 -#error "CAN1 not present in the selected device" -#endif - -#if STM32_CAN_USE_CAN2 && !STM32_HAS_CAN2 -#error "CAN2 not present in the selected device" -#endif - -#if !STM32_CAN_USE_CAN1 && !STM32_CAN_USE_CAN2 -#error "CAN driver activated but no CAN peripheral assigned" -#endif - -#if !STM32_CAN_USE_CAN1 && STM32_CAN_USE_CAN2 -#error "CAN2 requires CAN1, it cannot operate independently" -#endif - -#if CAN_USE_SLEEP_MODE && !CAN_SUPPORTS_SLEEP -#error "CAN sleep mode not supported in this architecture" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a transmission mailbox index. - */ -typedef uint32_t canmbx_t; - -/** - * @brief CAN transmission frame. - * @note Accessing the frame data as word16 or word32 is not portable because - * machine data endianness, it can be still useful for a quick filling. - */ -typedef struct { - struct { - uint8_t DLC:4; /**< @brief Data length. */ - uint8_t RTR:1; /**< @brief Frame type. */ - uint8_t IDE:1; /**< @brief Identifier type. */ - }; - union { - struct { - uint32_t SID:11; /**< @brief Standard identifier.*/ - }; - struct { - uint32_t EID:29; /**< @brief Extended identifier.*/ - }; - }; - union { - uint8_t data8[8]; /**< @brief Frame data. */ - uint16_t data16[4]; /**< @brief Frame data. */ - uint32_t data32[2]; /**< @brief Frame data. */ - }; -} CANTxFrame; - -/** - * @brief CAN received frame. - * @note Accessing the frame data as word16 or word32 is not portable because - * machine data endianness, it can be still useful for a quick filling. - */ -typedef struct { - struct { - uint8_t FMI; /**< @brief Filter id. */ - uint16_t TIME; /**< @brief Time stamp. */ - }; - struct { - uint8_t DLC:4; /**< @brief Data length. */ - uint8_t RTR:1; /**< @brief Frame type. */ - uint8_t IDE:1; /**< @brief Identifier type. */ - }; - union { - struct { - uint32_t SID:11; /**< @brief Standard identifier.*/ - }; - struct { - uint32_t EID:29; /**< @brief Extended identifier.*/ - }; - }; - union { - uint8_t data8[8]; /**< @brief Frame data. */ - uint16_t data16[4]; /**< @brief Frame data. */ - uint32_t data32[2]; /**< @brief Frame data. */ - }; -} CANRxFrame; - -/** - * @brief CAN filter. - * @note Refer to the STM32 reference manual for info about filters. - */ -typedef struct { - /** - * @brief Number of the filter to be programmed. - */ - uint32_t filter; - /** - * @brief Filter mode. - * @note This bit represent the CAN_FM1R register bit associated to this - * filter (0=mask mode, 1=list mode). - */ - uint32_t mode:1; - /** - * @brief Filter scale. - * @note This bit represent the CAN_FS1R register bit associated to this - * filter (0=16 bits mode, 1=32 bits mode). - */ - uint32_t scale:1; - /** - * @brief Filter mode. - * @note This bit represent the CAN_FFA1R register bit associated to this - * filter, must be set to zero in this version of the driver. - */ - uint32_t assignment:1; - /** - * @brief Filter register 1 (identifier). - */ - uint32_t register1; - /** - * @brief Filter register 2 (mask/identifier depending on mode=0/1). - */ - uint32_t register2; -} CANFilter; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /** - * @brief CAN MCR register initialization data. - * @note Some bits in this register are enforced by the driver regardless - * their status in this field. - */ - uint32_t mcr; - /** - * @brief CAN BTR register initialization data. - * @note Some bits in this register are enforced by the driver regardless - * their status in this field. - */ - uint32_t btr; -} CANConfig; - -/** - * @brief Structure representing an CAN driver. - */ -typedef struct { - /** - * @brief Driver state. - */ - canstate_t state; - /** - * @brief Current configuration data. - */ - const CANConfig *config; - /** - * @brief Transmission threads queue. - */ - threads_queue_t txqueue; - /** - * @brief Receive threads queue. - */ - threads_queue_t rxqueue; - /** - * @brief One or more frames become available. - * @note After broadcasting this event it will not be broadcasted again - * until the received frames queue has been completely emptied. It - * is not broadcasted for each received frame. It is - * responsibility of the application to empty the queue by - * repeatedly invoking @p canReceive() when listening to this event. - * This behavior minimizes the interrupt served by the system - * because CAN traffic. - * @note The flags associated to the listeners will indicate which - * receive mailboxes become non-empty. - */ - event_source_t rxfull_event; - /** - * @brief One or more transmission mailbox become available. - * @note The flags associated to the listeners will indicate which - * transmit mailboxes become empty. - * @note The upper 16 bits are transmission error flags associated - * to the transmit mailboxes. - * - */ - event_source_t txempty_event; - /** - * @brief A CAN bus error happened. - * @note The flags associated to the listeners will indicate that - * receive error(s) have occurred. - * @note In this implementation the upper 16 bits are filled with the - * unprocessed content of the ESR register. - */ - event_source_t error_event; -#if CAN_USE_SLEEP_MODE || defined (__DOXYGEN__) - /** - * @brief Entering sleep state event. - */ - event_source_t sleep_event; - /** - * @brief Exiting sleep state event. - */ - event_source_t wakeup_event; -#endif /* CAN_USE_SLEEP_MODE */ - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the CAN registers. - */ - CAN_TypeDef *can; -} CANDriver; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_CAN_USE_CAN1 && !defined(__DOXYGEN__) -extern CANDriver CAND1; -#endif - -#if STM32_CAN_USE_CAN2 && !defined(__DOXYGEN__) -extern CANDriver CAND2; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void can_lld_init(void); - void can_lld_start(CANDriver *canp); - void can_lld_stop(CANDriver *canp); - bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox); - void can_lld_transmit(CANDriver *canp, - canmbx_t mailbox, - const CANTxFrame *crfp); - bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox); - void can_lld_receive(CANDriver *canp, - canmbx_t mailbox, - CANRxFrame *ctfp); -#if CAN_USE_SLEEP_MODE - void can_lld_sleep(CANDriver *canp); - void can_lld_wakeup(CANDriver *canp); -#endif /* CAN_USE_SLEEP_MODE */ - void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_CAN */ - -#endif /* _CAN_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/CANv1/hal_can_lld.c b/os/hal/ports/STM32/LLD/CANv1/hal_can_lld.c new file mode 100644 index 000000000..9f68fdc25 --- /dev/null +++ b/os/hal/ports/STM32/LLD/CANv1/hal_can_lld.c @@ -0,0 +1,847 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/can_lld.c + * @brief STM32 CAN subsystem low level driver source. + * + * @addtogroup CAN + * @{ + */ + +#include "hal.h" + +#if HAL_USE_CAN || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/* + * Addressing differences in the headers, they seem unable to agree on names. + */ +#if STM32_CAN_USE_CAN1 +#if !defined(CAN1) +#define CAN1 CAN +#endif +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief CAN1 driver identifier.*/ +#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__) +CANDriver CAND1; +#endif + +/** @brief CAN2 driver identifier.*/ +#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__) +CANDriver CAND2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Programs the filters. + * + * @param[in] can2sb number of the first filter assigned to CAN2 + * @param[in] num number of entries in the filters array, if zero then + * a default filter is programmed + * @param[in] cfp pointer to the filters array, can be @p NULL if + * (num == 0) + * + * @notapi + */ +static void can_lld_set_filters(uint32_t can2sb, + uint32_t num, + const CANFilter *cfp) { + + /* Temporarily enabling CAN1 clock.*/ + rccEnableCAN1(FALSE); + + /* Filters initialization.*/ + CAN1->FMR = (CAN1->FMR & 0xFFFF0000) | (can2sb << 8) | CAN_FMR_FINIT; + if (num > 0) { + uint32_t i, fmask; + + /* All filters cleared.*/ + CAN1->FA1R = 0; + CAN1->FM1R = 0; + CAN1->FS1R = 0; + CAN1->FFA1R = 0; + for (i = 0; i < STM32_CAN_MAX_FILTERS; i++) { + CAN1->sFilterRegister[i].FR1 = 0; + CAN1->sFilterRegister[i].FR2 = 0; + } + + /* Scanning the filters array.*/ + for (i = 0; i < num; i++) { + fmask = 1 << cfp->filter; + if (cfp->mode) + CAN1->FM1R |= fmask; + if (cfp->scale) + CAN1->FS1R |= fmask; + if (cfp->assignment) + CAN1->FFA1R |= fmask; + CAN1->sFilterRegister[cfp->filter].FR1 = cfp->register1; + CAN1->sFilterRegister[cfp->filter].FR2 = cfp->register2; + CAN1->FA1R |= fmask; + cfp++; + } + } + else { + /* Setting up a single default filter that enables everything for both + CANs.*/ + CAN1->sFilterRegister[0].FR1 = 0; + CAN1->sFilterRegister[0].FR2 = 0; +#if STM32_HAS_CAN2 + CAN1->sFilterRegister[can2sb].FR1 = 0; + CAN1->sFilterRegister[can2sb].FR2 = 0; +#endif + CAN1->FM1R = 0; + CAN1->FFA1R = 0; +#if STM32_HAS_CAN2 + CAN1->FS1R = 1 | (1 << can2sb); + CAN1->FA1R = 1 | (1 << can2sb); +#else + CAN1->FS1R = 1; + CAN1->FA1R = 1; +#endif + } + CAN1->FMR &= ~CAN_FMR_FINIT; + + /* Clock disabled, it will be enabled again in can_lld_start().*/ + rccDisableCAN1(FALSE); +} + +/** + * @brief Common TX ISR handler. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +static void can_lld_tx_handler(CANDriver *canp) { + uint32_t tsr; + eventflags_t flags; + + /* Clearing IRQ sources.*/ + tsr = canp->can->TSR; + canp->can->TSR = tsr; + + /* Flags to be signaled through the TX event source.*/ + flags = 0U; + + /* Checking mailbox 0.*/ + if ((tsr & CAN_TSR_RQCP0) != 0U) { + if ((tsr & (CAN_TSR_ALST0 | CAN_TSR_TERR0)) != 0U) { + flags |= CAN_MAILBOX_TO_MASK(1U) << 16U; + } + else { + flags |= CAN_MAILBOX_TO_MASK(1U); + } + } + + /* Checking mailbox 1.*/ + if ((tsr & CAN_TSR_RQCP1) != 0U) { + if ((tsr & (CAN_TSR_ALST1 | CAN_TSR_TERR1)) != 0U) { + flags |= CAN_MAILBOX_TO_MASK(2U) << 16U; + } + else { + flags |= CAN_MAILBOX_TO_MASK(2U); + } + } + + /* Checking mailbox 2.*/ + if ((tsr & CAN_TSR_RQCP2) != 0U) { + if ((tsr & (CAN_TSR_ALST2 | CAN_TSR_TERR2)) != 0U) { + flags |= CAN_MAILBOX_TO_MASK(3U) << 16U; + } + else { + flags |= CAN_MAILBOX_TO_MASK(3U); + } + } + + /* Signaling flags and waking up threads waiting for a transmission slot.*/ + osalSysLockFromISR(); + osalThreadDequeueAllI(&canp->txqueue, MSG_OK); + osalEventBroadcastFlagsI(&canp->txempty_event, flags); + osalSysUnlockFromISR(); +} + +/** + * @brief Common RX0 ISR handler. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +static void can_lld_rx0_handler(CANDriver *canp) { + uint32_t rf0r; + + rf0r = canp->can->RF0R; + if ((rf0r & CAN_RF0R_FMP0) > 0) { + /* No more receive events until the queue 0 has been emptied.*/ + canp->can->IER &= ~CAN_IER_FMPIE0; + osalSysLockFromISR(); + osalThreadDequeueAllI(&canp->rxqueue, MSG_OK); + osalEventBroadcastFlagsI(&canp->rxfull_event, CAN_MAILBOX_TO_MASK(1U)); + osalSysUnlockFromISR(); + } + if ((rf0r & CAN_RF0R_FOVR0) > 0) { + /* Overflow events handling.*/ + canp->can->RF0R = CAN_RF0R_FOVR0; + osalSysLockFromISR(); + osalEventBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR); + osalSysUnlockFromISR(); + } +} + +/** + * @brief Common RX1 ISR handler. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +static void can_lld_rx1_handler(CANDriver *canp) { + uint32_t rf1r; + + rf1r = canp->can->RF1R; + if ((rf1r & CAN_RF1R_FMP1) > 0) { + /* No more receive events until the queue 0 has been emptied.*/ + canp->can->IER &= ~CAN_IER_FMPIE1; + osalSysLockFromISR(); + osalThreadDequeueAllI(&canp->rxqueue, MSG_OK); + osalEventBroadcastFlagsI(&canp->rxfull_event, CAN_MAILBOX_TO_MASK(2U)); + osalSysUnlockFromISR(); + } + if ((rf1r & CAN_RF1R_FOVR1) > 0) { + /* Overflow events handling.*/ + canp->can->RF1R = CAN_RF1R_FOVR1; + osalSysLockFromISR(); + osalEventBroadcastFlagsI(&canp->error_event, CAN_OVERFLOW_ERROR); + osalSysUnlockFromISR(); + } +} + +/** + * @brief Common SCE ISR handler. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +static void can_lld_sce_handler(CANDriver *canp) { + uint32_t msr; + + /* Clearing IRQ sources.*/ + msr = canp->can->MSR; + canp->can->MSR = msr; + + /* Wakeup event.*/ +#if CAN_USE_SLEEP_MODE + if (msr & CAN_MSR_WKUI) { + canp->state = CAN_READY; + canp->can->MCR &= ~CAN_MCR_SLEEP; + osalSysLockFromISR(); + osalEventBroadcastFlagsI(&canp->wakeup_event, 0); + osalSysUnlockFromISR(); + } +#endif /* CAN_USE_SLEEP_MODE */ + /* Error event.*/ + if (msr & CAN_MSR_ERRI) { + eventflags_t flags; + uint32_t esr = canp->can->ESR; + +#if STM32_CAN_REPORT_ALL_ERRORS + flags = (eventflags_t)(esr & 7); + if ((esr & CAN_ESR_LEC) > 0) + flags |= CAN_FRAMING_ERROR; +#else + flags = 0; +#endif + + osalSysLockFromISR(); + /* The content of the ESR register is copied unchanged in the upper + half word of the listener flags mask.*/ + osalEventBroadcastFlagsI(&canp->error_event, + flags | (eventflags_t)(esr << 16U)); + osalSysUnlockFromISR(); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_CAN_USE_CAN1 || defined(__DOXYGEN__) +#if defined(STM32_CAN1_UNIFIED_HANDLER) +/** + * @brief CAN1 unified interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN1_UNIFIED_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_tx_handler(&CAND1); + can_lld_rx0_handler(&CAND1); + can_lld_rx1_handler(&CAND1); + can_lld_sce_handler(&CAND1); + + OSAL_IRQ_EPILOGUE(); +} +#else /* !defined(STM32_CAN1_UNIFIED_HANDLER) */ + +#if !defined(STM32_CAN1_TX_HANDLER) +#error "STM32_CAN1_TX_HANDLER not defined" +#endif +#if !defined(STM32_CAN1_RX0_HANDLER) +#error "STM32_CAN1_RX0_HANDLER not defined" +#endif +#if !defined(STM32_CAN1_RX1_HANDLER) +#error "STM32_CAN1_RX1_HANDLER not defined" +#endif +#if !defined(STM32_CAN1_SCE_HANDLER) +#error "STM32_CAN1_SCE_HANDLER not defined" +#endif + +/** + * @brief CAN1 TX interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN1_TX_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_tx_handler(&CAND1); + + OSAL_IRQ_EPILOGUE(); +} + +/* + * @brief CAN1 RX0 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN1_RX0_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_rx0_handler(&CAND1); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief CAN1 RX1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN1_RX1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_rx1_handler(&CAND1); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief CAN1 SCE interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN1_SCE_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_sce_handler(&CAND1); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_CAN1_UNIFIED_HANDLER) */ +#endif /* STM32_CAN_USE_CAN1 */ + +#if STM32_CAN_USE_CAN2 || defined(__DOXYGEN__) +#if defined(STM32_CAN2_UNIFIED_HANDLER) +/** + * @brief CAN1 unified interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN2_UNIFIED_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_tx_handler(&CAND2); + can_lld_rx0_handler(&CAND2); + can_lld_rx1_handler(&CAND2); + can_lld_sce_handler(&CAND2); + + OSAL_IRQ_EPILOGUE(); +} +#else /* !defined(STM32_CAN2_UNIFIED_HANDLER) */ + +#if !defined(STM32_CAN1_TX_HANDLER) +#error "STM32_CAN1_TX_HANDLER not defined" +#endif +#if !defined(STM32_CAN1_RX0_HANDLER) +#error "STM32_CAN1_RX0_HANDLER not defined" +#endif +#if !defined(STM32_CAN1_RX1_HANDLER) +#error "STM32_CAN1_RX1_HANDLER not defined" +#endif +#if !defined(STM32_CAN1_SCE_HANDLER) +#error "STM32_CAN1_SCE_HANDLER not defined" +#endif + +/** + * @brief CAN2 TX interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN2_TX_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_tx_handler(&CAND2); + + OSAL_IRQ_EPILOGUE(); +} + +/* + * @brief CAN2 RX0 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN2_RX0_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_rx0_handler(&CAND2); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief CAN2 RX1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN2_RX1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_rx1_handler(&CAND2); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief CAN2 SCE interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_CAN2_SCE_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + can_lld_sce_handler(&CAND2); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_CAN2_UNIFIED_HANDLER) */ +#endif /* STM32_CAN_USE_CAN2 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level CAN driver initialization. + * + * @notapi + */ +void can_lld_init(void) { + +#if STM32_CAN_USE_CAN1 + /* Driver initialization.*/ + canObjectInit(&CAND1); + CAND1.can = CAN1; +#endif +#if STM32_CAN_USE_CAN2 + /* Driver initialization.*/ + canObjectInit(&CAND2); + CAND2.can = CAN2; +#endif + + /* Filters initialization.*/ +#if STM32_HAS_CAN2 + can_lld_set_filters(STM32_CAN_MAX_FILTERS / 2, 0, NULL); +#else + can_lld_set_filters(STM32_CAN_MAX_FILTERS, 0, NULL); +#endif +} + +/** + * @brief Configures and activates the CAN peripheral. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_start(CANDriver *canp) { + + /* Clock activation.*/ +#if STM32_CAN_USE_CAN1 + if (&CAND1 == canp) { +#if defined(STM32_CAN1_UNIFIED_NUMBER) + nvicEnableVector(STM32_CAN1_UNIFIED_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); +#else + nvicEnableVector(STM32_CAN1_TX_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); + nvicEnableVector(STM32_CAN1_RX0_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); + nvicEnableVector(STM32_CAN1_RX1_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); + nvicEnableVector(STM32_CAN1_SCE_NUMBER, STM32_CAN_CAN1_IRQ_PRIORITY); +#endif + rccEnableCAN1(FALSE); + } +#endif +#if STM32_CAN_USE_CAN2 + if (&CAND2 == canp) { + + osalDbgAssert(CAND1.state != CAN_STOP, "CAN1 must be started"); + +#if defined(STM32_CAN2_UNIFIED_NUMBER) + nvicEnableVector(STM32_CAN2_UNIFIED_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); +#else + nvicEnableVector(STM32_CAN2_TX_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); + nvicEnableVector(STM32_CAN2_RX0_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); + nvicEnableVector(STM32_CAN2_RX1_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); + nvicEnableVector(STM32_CAN2_SCE_NUMBER, STM32_CAN_CAN2_IRQ_PRIORITY); +#endif + rccEnableCAN2(FALSE); + } +#endif + + /* Configuring CAN. */ + canp->can->MCR = CAN_MCR_INRQ; + while ((canp->can->MSR & CAN_MSR_INAK) == 0) + osalThreadSleepS(1); + canp->can->BTR = canp->config->btr; + canp->can->MCR = canp->config->mcr; + + /* Interrupt sources initialization.*/ +#if STM32_CAN_REPORT_ALL_ERRORS + canp->can->IER = CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | + CAN_IER_WKUIE | CAN_IER_ERRIE | CAN_IER_LECIE | + CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE | + CAN_IER_FOVIE0 | CAN_IER_FOVIE1; +#else + canp->can->IER = CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | + CAN_IER_WKUIE | CAN_IER_ERRIE | + CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE | + CAN_IER_FOVIE0 | CAN_IER_FOVIE1; +#endif +} + +/** + * @brief Deactivates the CAN peripheral. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_stop(CANDriver *canp) { + + /* If in ready state then disables the CAN peripheral.*/ + if (canp->state == CAN_READY) { +#if STM32_CAN_USE_CAN1 + if (&CAND1 == canp) { + +#if STM32_CAN_USE_CAN2 + osalDbgAssert(CAND2.state == CAN_STOP, "CAN2 must be stopped"); +#endif + + CAN1->MCR = 0x00010002; /* Register reset value. */ + CAN1->IER = 0x00000000; /* All sources disabled. */ +#if defined(STM32_CAN1_UNIFIED_NUMBER) + nvicDisableVector(STM32_CAN1_UNIFIED_NUMBER); +#else + nvicDisableVector(STM32_CAN1_TX_NUMBER); + nvicDisableVector(STM32_CAN1_RX0_NUMBER); + nvicDisableVector(STM32_CAN1_RX1_NUMBER); + nvicDisableVector(STM32_CAN1_SCE_NUMBER); +#endif + rccDisableCAN1(FALSE); + } +#endif +#if STM32_CAN_USE_CAN2 + if (&CAND2 == canp) { + CAN2->MCR = 0x00010002; /* Register reset value. */ + CAN2->IER = 0x00000000; /* All sources disabled. */ +#if defined(STM32_CAN2_UNIFIED_NUMBER) + nvicDisableVector(STM32_CAN2_UNIFIED_NUMBER); +#else + nvicDisableVector(STM32_CAN2_TX_NUMBER); + nvicDisableVector(STM32_CAN2_RX0_NUMBER); + nvicDisableVector(STM32_CAN2_RX1_NUMBER); + nvicDisableVector(STM32_CAN2_SCE_NUMBER); +#endif + rccDisableCAN2(FALSE); + } +#endif + } +} + +/** + * @brief Determines whether a frame can be transmitted. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * + * @return The queue space availability. + * @retval FALSE no space in the transmit queue. + * @retval TRUE transmit slot available. + * + * @notapi + */ +bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox) { + + switch (mailbox) { + case CAN_ANY_MAILBOX: + return (canp->can->TSR & CAN_TSR_TME) != 0; + case 1: + return (canp->can->TSR & CAN_TSR_TME0) != 0; + case 2: + return (canp->can->TSR & CAN_TSR_TME1) != 0; + case 3: + return (canp->can->TSR & CAN_TSR_TME2) != 0; + default: + return FALSE; + } +} + +/** + * @brief Inserts a frame into the transmit queue. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] ctfp pointer to the CAN frame to be transmitted + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * + * @notapi + */ +void can_lld_transmit(CANDriver *canp, + canmbx_t mailbox, + const CANTxFrame *ctfp) { + uint32_t tir; + CAN_TxMailBox_TypeDef *tmbp; + + /* Pointer to a free transmission mailbox.*/ + switch (mailbox) { + case CAN_ANY_MAILBOX: + tmbp = &canp->can->sTxMailBox[(canp->can->TSR & CAN_TSR_CODE) >> 24]; + break; + case 1: + tmbp = &canp->can->sTxMailBox[0]; + break; + case 2: + tmbp = &canp->can->sTxMailBox[1]; + break; + case 3: + tmbp = &canp->can->sTxMailBox[2]; + break; + default: + return; + } + + /* Preparing the message.*/ + if (ctfp->IDE) + tir = ((uint32_t)ctfp->EID << 3) | ((uint32_t)ctfp->RTR << 1) | + CAN_TI0R_IDE; + else + tir = ((uint32_t)ctfp->SID << 21) | ((uint32_t)ctfp->RTR << 1); + tmbp->TDTR = ctfp->DLC; + tmbp->TDLR = ctfp->data32[0]; + tmbp->TDHR = ctfp->data32[1]; + tmbp->TIR = tir | CAN_TI0R_TXRQ; +} + +/** + * @brief Determines whether a frame has been received. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * + * @return The queue space availability. + * @retval FALSE no space in the transmit queue. + * @retval TRUE transmit slot available. + * + * @notapi + */ +bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox) { + + switch (mailbox) { + case CAN_ANY_MAILBOX: + return ((canp->can->RF0R & CAN_RF0R_FMP0) != 0 || + (canp->can->RF1R & CAN_RF1R_FMP1) != 0); + case 1: + return (canp->can->RF0R & CAN_RF0R_FMP0) != 0; + case 2: + return (canp->can->RF1R & CAN_RF1R_FMP1) != 0; + default: + return FALSE; + } +} + +/** + * @brief Receives a frame from the input queue. + * + * @param[in] canp pointer to the @p CANDriver object + * @param[in] mailbox mailbox number, @p CAN_ANY_MAILBOX for any mailbox + * @param[out] crfp pointer to the buffer where the CAN frame is copied + * + * @notapi + */ +void can_lld_receive(CANDriver *canp, + canmbx_t mailbox, + CANRxFrame *crfp) { + uint32_t rir, rdtr; + + if (mailbox == CAN_ANY_MAILBOX) { + if ((canp->can->RF0R & CAN_RF0R_FMP0) != 0) + mailbox = 1; + else if ((canp->can->RF1R & CAN_RF1R_FMP1) != 0) + mailbox = 2; + else { + /* Should not happen, do nothing.*/ + return; + } + } + switch (mailbox) { + case 1: + /* Fetches the message.*/ + rir = canp->can->sFIFOMailBox[0].RIR; + rdtr = canp->can->sFIFOMailBox[0].RDTR; + crfp->data32[0] = canp->can->sFIFOMailBox[0].RDLR; + crfp->data32[1] = canp->can->sFIFOMailBox[0].RDHR; + + /* Releases the mailbox.*/ + canp->can->RF0R = CAN_RF0R_RFOM0; + + /* If the queue is empty re-enables the interrupt in order to generate + events again.*/ + if ((canp->can->RF0R & CAN_RF0R_FMP0) == 0) + canp->can->IER |= CAN_IER_FMPIE0; + break; + case 2: + /* Fetches the message.*/ + rir = canp->can->sFIFOMailBox[1].RIR; + rdtr = canp->can->sFIFOMailBox[1].RDTR; + crfp->data32[0] = canp->can->sFIFOMailBox[1].RDLR; + crfp->data32[1] = canp->can->sFIFOMailBox[1].RDHR; + + /* Releases the mailbox.*/ + canp->can->RF1R = CAN_RF1R_RFOM1; + + /* If the queue is empty re-enables the interrupt in order to generate + events again.*/ + if ((canp->can->RF1R & CAN_RF1R_FMP1) == 0) + canp->can->IER |= CAN_IER_FMPIE1; + break; + default: + /* Should not happen, do nothing.*/ + return; + } + + /* Decodes the various fields in the RX frame.*/ + crfp->RTR = (rir & CAN_RI0R_RTR) >> 1; + crfp->IDE = (rir & CAN_RI0R_IDE) >> 2; + if (crfp->IDE) + crfp->EID = rir >> 3; + else + crfp->SID = rir >> 21; + crfp->DLC = rdtr & CAN_RDT0R_DLC; + crfp->FMI = (uint8_t)(rdtr >> 8); + crfp->TIME = (uint16_t)(rdtr >> 16); +} + +#if CAN_USE_SLEEP_MODE || defined(__DOXYGEN__) +/** + * @brief Enters the sleep mode. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_sleep(CANDriver *canp) { + + canp->can->MCR |= CAN_MCR_SLEEP; +} + +/** + * @brief Enforces leaving the sleep mode. + * + * @param[in] canp pointer to the @p CANDriver object + * + * @notapi + */ +void can_lld_wakeup(CANDriver *canp) { + + canp->can->MCR &= ~CAN_MCR_SLEEP; +} +#endif /* CAN_USE_SLEEP_MODE */ + +/** + * @brief Programs the filters. + * @note This is an STM32-specific API. + * + * @param[in] can2sb number of the first filter assigned to CAN2 + * @param[in] num number of entries in the filters array, if zero then + * a default filter is programmed + * @param[in] cfp pointer to the filters array, can be @p NULL if + * (num == 0) + * + * @api + */ +void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp) { + + osalDbgCheck((can2sb >= 1) && (can2sb < STM32_CAN_MAX_FILTERS) && + (num <= STM32_CAN_MAX_FILTERS)); + +#if STM32_CAN_USE_CAN1 + osalDbgAssert(CAND1.state == CAN_STOP, "invalid state"); +#endif +#if STM32_CAN_USE_CAN2 + osalDbgAssert(CAND2.state == CAN_STOP, "invalid state"); +#endif + + can_lld_set_filters(can2sb, num, cfp); +} + +#endif /* HAL_USE_CAN */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/CANv1/hal_can_lld.h b/os/hal/ports/STM32/LLD/CANv1/hal_can_lld.h new file mode 100644 index 000000000..824c1a66d --- /dev/null +++ b/os/hal/ports/STM32/LLD/CANv1/hal_can_lld.h @@ -0,0 +1,377 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/can_lld.h + * @brief STM32 CAN subsystem low level driver header. + * + * @addtogroup CAN + * @{ + */ + +#ifndef _CAN_LLD_H_ +#define _CAN_LLD_H_ + +#if HAL_USE_CAN || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/* + * The following macros from the ST header file are replaced with better + * equivalents. + */ +#undef CAN_BTR_BRP +#undef CAN_BTR_TS1 +#undef CAN_BTR_TS2 +#undef CAN_BTR_SJW + +/** + * @brief This switch defines whether the driver implementation supports + * a low power switch mode with automatic an wakeup feature. + */ +#define CAN_SUPPORTS_SLEEP TRUE + +/** + * @brief This implementation supports three transmit mailboxes. + */ +#define CAN_TX_MAILBOXES 3 + +/** + * @brief This implementation supports two receive mailboxes. + */ +#define CAN_RX_MAILBOXES 2 + +/** + * @name CAN registers helper macros + * @{ + */ +#define CAN_BTR_BRP(n) (n) /**< @brief BRP field macro.*/ +#define CAN_BTR_TS1(n) ((n) << 16) /**< @brief TS1 field macro.*/ +#define CAN_BTR_TS2(n) ((n) << 20) /**< @brief TS2 field macro.*/ +#define CAN_BTR_SJW(n) ((n) << 24) /**< @brief SJW field macro.*/ + +#define CAN_IDE_STD 0 /**< @brief Standard id. */ +#define CAN_IDE_EXT 1 /**< @brief Extended id. */ + +#define CAN_RTR_DATA 0 /**< @brief Data frame. */ +#define CAN_RTR_REMOTE 1 /**< @brief Remote frame. */ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief CAN pedantic errors report. + * @details Use of this option is IRQ-intensive. + */ +#if !defined(STM32_CAN_REPORT_ALL_ERRORS) || defined(__DOXYGEN__) +#define STM32_CAN_REPORT_ALL_ERRORS FALSE +#endif + +/** + * @brief CAN1 driver enable switch. + * @details If set to @p TRUE the support for CAN1 is included. + */ +#if !defined(STM32_CAN_USE_CAN1) || defined(__DOXYGEN__) +#define STM32_CAN_USE_CAN1 FALSE +#endif + +/** + * @brief CAN2 driver enable switch. + * @details If set to @p TRUE the support for CAN2 is included. + */ +#if !defined(STM32_CAN_USE_CAN2) || defined(__DOXYGEN__) +#define STM32_CAN_USE_CAN2 FALSE +#endif + +/** + * @brief CAN1 interrupt priority level setting. + */ +#if !defined(STM32_CAN_CAN1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_CAN_CAN1_IRQ_PRIORITY 11 +#endif +/** @} */ + +/** + * @brief CAN2 interrupt priority level setting. + */ +#if !defined(STM32_CAN_CAN2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_CAN_CAN2_IRQ_PRIORITY 11 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_CAN_USE_CAN1 && !STM32_HAS_CAN1 +#error "CAN1 not present in the selected device" +#endif + +#if STM32_CAN_USE_CAN2 && !STM32_HAS_CAN2 +#error "CAN2 not present in the selected device" +#endif + +#if !STM32_CAN_USE_CAN1 && !STM32_CAN_USE_CAN2 +#error "CAN driver activated but no CAN peripheral assigned" +#endif + +#if !STM32_CAN_USE_CAN1 && STM32_CAN_USE_CAN2 +#error "CAN2 requires CAN1, it cannot operate independently" +#endif + +#if CAN_USE_SLEEP_MODE && !CAN_SUPPORTS_SLEEP +#error "CAN sleep mode not supported in this architecture" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a transmission mailbox index. + */ +typedef uint32_t canmbx_t; + +/** + * @brief CAN transmission frame. + * @note Accessing the frame data as word16 or word32 is not portable because + * machine data endianness, it can be still useful for a quick filling. + */ +typedef struct { + struct { + uint8_t DLC:4; /**< @brief Data length. */ + uint8_t RTR:1; /**< @brief Frame type. */ + uint8_t IDE:1; /**< @brief Identifier type. */ + }; + union { + struct { + uint32_t SID:11; /**< @brief Standard identifier.*/ + }; + struct { + uint32_t EID:29; /**< @brief Extended identifier.*/ + }; + }; + union { + uint8_t data8[8]; /**< @brief Frame data. */ + uint16_t data16[4]; /**< @brief Frame data. */ + uint32_t data32[2]; /**< @brief Frame data. */ + }; +} CANTxFrame; + +/** + * @brief CAN received frame. + * @note Accessing the frame data as word16 or word32 is not portable because + * machine data endianness, it can be still useful for a quick filling. + */ +typedef struct { + struct { + uint8_t FMI; /**< @brief Filter id. */ + uint16_t TIME; /**< @brief Time stamp. */ + }; + struct { + uint8_t DLC:4; /**< @brief Data length. */ + uint8_t RTR:1; /**< @brief Frame type. */ + uint8_t IDE:1; /**< @brief Identifier type. */ + }; + union { + struct { + uint32_t SID:11; /**< @brief Standard identifier.*/ + }; + struct { + uint32_t EID:29; /**< @brief Extended identifier.*/ + }; + }; + union { + uint8_t data8[8]; /**< @brief Frame data. */ + uint16_t data16[4]; /**< @brief Frame data. */ + uint32_t data32[2]; /**< @brief Frame data. */ + }; +} CANRxFrame; + +/** + * @brief CAN filter. + * @note Refer to the STM32 reference manual for info about filters. + */ +typedef struct { + /** + * @brief Number of the filter to be programmed. + */ + uint32_t filter; + /** + * @brief Filter mode. + * @note This bit represent the CAN_FM1R register bit associated to this + * filter (0=mask mode, 1=list mode). + */ + uint32_t mode:1; + /** + * @brief Filter scale. + * @note This bit represent the CAN_FS1R register bit associated to this + * filter (0=16 bits mode, 1=32 bits mode). + */ + uint32_t scale:1; + /** + * @brief Filter mode. + * @note This bit represent the CAN_FFA1R register bit associated to this + * filter, must be set to zero in this version of the driver. + */ + uint32_t assignment:1; + /** + * @brief Filter register 1 (identifier). + */ + uint32_t register1; + /** + * @brief Filter register 2 (mask/identifier depending on mode=0/1). + */ + uint32_t register2; +} CANFilter; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief CAN MCR register initialization data. + * @note Some bits in this register are enforced by the driver regardless + * their status in this field. + */ + uint32_t mcr; + /** + * @brief CAN BTR register initialization data. + * @note Some bits in this register are enforced by the driver regardless + * their status in this field. + */ + uint32_t btr; +} CANConfig; + +/** + * @brief Structure representing an CAN driver. + */ +typedef struct { + /** + * @brief Driver state. + */ + canstate_t state; + /** + * @brief Current configuration data. + */ + const CANConfig *config; + /** + * @brief Transmission threads queue. + */ + threads_queue_t txqueue; + /** + * @brief Receive threads queue. + */ + threads_queue_t rxqueue; + /** + * @brief One or more frames become available. + * @note After broadcasting this event it will not be broadcasted again + * until the received frames queue has been completely emptied. It + * is not broadcasted for each received frame. It is + * responsibility of the application to empty the queue by + * repeatedly invoking @p canReceive() when listening to this event. + * This behavior minimizes the interrupt served by the system + * because CAN traffic. + * @note The flags associated to the listeners will indicate which + * receive mailboxes become non-empty. + */ + event_source_t rxfull_event; + /** + * @brief One or more transmission mailbox become available. + * @note The flags associated to the listeners will indicate which + * transmit mailboxes become empty. + * @note The upper 16 bits are transmission error flags associated + * to the transmit mailboxes. + * + */ + event_source_t txempty_event; + /** + * @brief A CAN bus error happened. + * @note The flags associated to the listeners will indicate that + * receive error(s) have occurred. + * @note In this implementation the upper 16 bits are filled with the + * unprocessed content of the ESR register. + */ + event_source_t error_event; +#if CAN_USE_SLEEP_MODE || defined (__DOXYGEN__) + /** + * @brief Entering sleep state event. + */ + event_source_t sleep_event; + /** + * @brief Exiting sleep state event. + */ + event_source_t wakeup_event; +#endif /* CAN_USE_SLEEP_MODE */ + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the CAN registers. + */ + CAN_TypeDef *can; +} CANDriver; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_CAN_USE_CAN1 && !defined(__DOXYGEN__) +extern CANDriver CAND1; +#endif + +#if STM32_CAN_USE_CAN2 && !defined(__DOXYGEN__) +extern CANDriver CAND2; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void can_lld_init(void); + void can_lld_start(CANDriver *canp); + void can_lld_stop(CANDriver *canp); + bool can_lld_is_tx_empty(CANDriver *canp, canmbx_t mailbox); + void can_lld_transmit(CANDriver *canp, + canmbx_t mailbox, + const CANTxFrame *crfp); + bool can_lld_is_rx_nonempty(CANDriver *canp, canmbx_t mailbox); + void can_lld_receive(CANDriver *canp, + canmbx_t mailbox, + CANRxFrame *ctfp); +#if CAN_USE_SLEEP_MODE + void can_lld_sleep(CANDriver *canp); + void can_lld_wakeup(CANDriver *canp); +#endif /* CAN_USE_SLEEP_MODE */ + void canSTM32SetFilters(uint32_t can2sb, uint32_t num, const CANFilter *cfp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_CAN */ + +#endif /* _CAN_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/DACv1/dac_lld.c b/os/hal/ports/STM32/LLD/DACv1/dac_lld.c deleted file mode 100644 index aae757cc0..000000000 --- a/os/hal/ports/STM32/LLD/DACv1/dac_lld.c +++ /dev/null @@ -1,515 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/DACv1/dac_lld.c - * @brief STM32 DAC subsystem low level driver source. - * - * @addtogroup DAC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_DAC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/* Because ST headers naming inconsistencies.*/ -#if !defined(DAC1) -#define DAC1 DAC -#endif - -#define DAC1_CH1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH1_DMA_STREAM, \ - STM32_DAC1_CH1_DMA_CHN) - -#define DAC1_CH2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH2_DMA_STREAM, \ - STM32_DAC1_CH2_DMA_CHN) - -#define DAC2_CH1_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH1_DMA_STREAM, \ - STM32_DAC2_CH1_DMA_CHN) - -#define DAC2_CH2_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH2_DMA_STREAM, \ - STM32_DAC2_CH2_DMA_CHN) - -#define CHANNEL_DATA_OFFSET 3U - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief DAC1 CH1 driver identifier.*/ -#if STM32_DAC_USE_DAC1_CH1 || defined(__DOXYGEN__) -DACDriver DACD1; -#endif - -/** @brief DAC1 CH2 driver identifier.*/ -#if (STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -DACDriver DACD2; -#endif - -/** @brief DAC2 CH1 driver identifier.*/ -#if STM32_DAC_USE_DAC2_CH1 || defined(__DOXYGEN__) -DACDriver DACD3; -#endif - -/** @brief DAC2 CH2 driver identifier.*/ -#if (STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -DACDriver DACD4; -#endif - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -#if STM32_DAC_USE_DAC1_CH1 == TRUE -static const dacparams_t dma1_ch1_params = { - .dac = DAC1, - .dataoffset = 0U, - .regshift = 0U, - .regmask = 0xFFFF0000U, - .dma = STM32_DMA_STREAM(STM32_DAC_DAC1_CH1_DMA_STREAM), - .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC1_CH1_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC1_CH1_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC1_CH2 == TRUE -static const dacparams_t dma1_ch2_params = { - .dac = DAC1, - .dataoffset = CHANNEL_DATA_OFFSET, - .regshift = 16U, - .regmask = 0x0000FFFFU, - .dma = STM32_DMA_STREAM(STM32_DAC_DAC1_CH2_DMA_STREAM), - .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC1_CH2_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC1_CH2_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC2_CH1 == TRUE -static const dacparams_t dma2_ch1_params = { - .dac = DAC2, - .dataoffset = 0U, - .regshift = 0U, - .regmask = 0xFFFF0000U, - .dma = STM32_DMA_STREAM(STM32_DAC_DAC2_CH1_DMA_STREAM), - .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH1_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC2_CH1_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC2_CH1_IRQ_PRIORITY -}; -#endif - -#if STM32_DAC_USE_DAC2_CH2 == TRUE -static const dacparams_t dma1_ch2_params = { - .dac = DAC2, - .dataoffset = CHANNEL_DATA_OFFSET, - .regshift = 16U, - .regmask = 0x0000FFFFU, - .dma = STM32_DMA_STREAM(STM32_DAC_DAC2_CH2_DMA_STREAM), - .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH2_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_DAC_DAC2_CH2_DMA_PRIORITY) | - STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE, - .dmairqprio = STM32_DAC_DAC2_CH2_IRQ_PRIORITY -}; -#endif - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Shared end/half-of-tx service routine. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void dac_lld_serve_tx_interrupt(DACDriver *dacp, uint32_t flags) { - - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - /* DMA errors handling.*/ - _dac_isr_error_code(dacp, DAC_ERR_DMAFAILURE); - } - else { - if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _dac_isr_half_code(dacp); - } - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _dac_isr_full_code(dacp); - } - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level DAC driver initialization. - * - * @notapi - */ -void dac_lld_init(void) { - -#if STM32_DAC_USE_DAC1_CH1 - dacObjectInit(&DACD1); - DACD1.params = &dma1_ch1_params; -#endif - -#if STM32_DAC_USE_DAC1_CH2 - dacObjectInit(&DACD2); - DACD2.params = &dma1_ch2_params; -#endif - -#if STM32_DAC_USE_DAC2_CH1 - dacObjectInit(&DACD3); - DACD3.params = &dma2_ch1_params; -#endif - -#if STM32_DAC_USE_DAC2_CH2 - dacObjectInit(&DACD4); - DACD4.params = &dma2_ch2_params; -#endif -} - -/** - * @brief Configures and activates the DAC peripheral. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -void dac_lld_start(DACDriver *dacp) { - - /* If the driver is in DAC_STOP state then a full initialization is - required.*/ - if (dacp->state == DAC_STOP) { - /* Enabling the clock source.*/ -#if STM32_DAC_USE_DAC1_CH1 - if (&DACD1 == dacp) { - rccEnableDAC1(false); - } -#endif - -#if STM32_DAC_USE_DAC1_CH2 - if (&DACD2 == dacp) { - rccEnableDAC1(false); - } -#endif - -#if STM32_DAC_USE_DAC2_CH1 - if (&DACD3 == dacp) { - rccEnableDAC2(false); - } -#endif - -#if STM32_DAC_USE_DAC2_CH2 - if (&DACD3 == dacp) { - rccEnableDAC2(false); - } -#endif - - /* Enabling DAC in SW triggering mode initially, initializing data to - zero.*/ -#if STM32_DAC_DUAL_MODE == FALSE - dacp->params->dac->CR &= dacp->params->regmask; - dacp->params->dac->CR |= DAC_CR_EN1 << dacp->params->regshift; - dac_lld_put_channel(dacp, 0U, dacp->config->init); -#else - if ((dacp->config->datamode == DAC_DHRM_12BIT_RIGHT_DUAL) || - (dacp->config->datamode == DAC_DHRM_12BIT_LEFT_DUAL) || - (dacp->config->datamode == DAC_DHRM_8BIT_RIGHT_DUAL)) { - dacp->params->dac->CR = DAC_CR_EN2 | DAC_CR_EN1; - dac_lld_put_channel(dacp, 1U, dacp->config->init); - } - else { - dacp->params->dac->CR = DAC_CR_EN1; - } - dac_lld_put_channel(dacp, 0U, dacp->config->init); -#endif - } -} - -/** - * @brief Deactivates the DAC peripheral. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -void dac_lld_stop(DACDriver *dacp) { - - /* If in ready state then disables the DAC clock.*/ - if (dacp->state == DAC_READY) { - - /* Disabling DAC.*/ - dacp->params->dac->CR &= dacp->params->regmask; - -#if STM32_DAC_USE_DAC1_CH1 - if (&DACD1 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { - rccDisableDAC1(false); - } - } -#endif - -#if STM32_DAC_USE_DAC1_CH2 - if (&DACD2 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { - rccDisableDAC1(false); - } - } -#endif - -#if STM32_DAC_USE_DAC2_CH1 - if (&DACD3 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { - rccDisableDAC2(false); - } - } -#endif - -#if STM32_DAC_USE_DAC2_CH2 - if (&DACD4 == dacp) { - if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { - rccDisableDAC2(false); - } - } -#endif - } -} - -/** - * @brief Outputs a value directly on a DAC channel. - * - * @param[in] dacp pointer to the @p DACDriver object - * @param[in] channel DAC channel number - * @param[in] sample value to be output - * - * @api - */ -void dac_lld_put_channel(DACDriver *dacp, - dacchannel_t channel, - dacsample_t sample) { - - switch (dacp->config->datamode) { - case DAC_DHRM_12BIT_RIGHT: -#if STM32_DAC_DUAL_MODE - case DAC_DHRM_12BIT_RIGHT_DUAL: -#endif - if (channel == 0U) { - dacp->params->dac->DHR12R1 = (uint32_t)sample; - } - else { - dacp->params->dac->DHR12R2 = (uint32_t)sample; - } - break; - case DAC_DHRM_12BIT_LEFT: -#if STM32_DAC_DUAL_MODE - case DAC_DHRM_12BIT_LEFT_DUAL: -#endif - if (channel == 0U) { - dacp->params->dac->DHR12L1 = (uint32_t)sample; - } - else { - dacp->params->dac->DHR12L2 = (uint32_t)sample; - } - break; - case DAC_DHRM_8BIT_RIGHT: -#if STM32_DAC_DUAL_MODE - case DAC_DHRM_8BIT_RIGHT_DUAL: -#endif - if (channel == 0U) { - dacp->params->dac->DHR8R1 = (uint32_t)sample; - } - else { - dacp->params->dac->DHR8R2 = (uint32_t)sample; - } - break; - default: - osalDbgAssert(false, "unexpected DAC mode"); - break; - } -} - -/** - * @brief Starts a DAC conversion. - * @details Starts an asynchronous conversion operation. - * @note In @p DAC_DHRM_8BIT_RIGHT mode the parameters passed to the - * callback are wrong because two samples are packed in a single - * dacsample_t element. This will not be corrected, do not rely - * on those parameters. - * @note In @p DAC_DHRM_8BIT_RIGHT_DUAL mode two samples are treated - * as a single 16 bits sample and packed into a single dacsample_t - * element. The num_channels must be set to one in the group - * conversion configuration structure. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @notapi - */ -void dac_lld_start_conversion(DACDriver *dacp) { - uint32_t n, cr, dmamode; - - /* Number of DMA operations per buffer.*/ - n = dacp->depth * dacp->grpp->num_channels; - - /* Allocating the DMA channel.*/ - bool b = dmaStreamAllocate(dacp->params->dma, dacp->params->dmairqprio, - (stm32_dmaisr_t)dac_lld_serve_tx_interrupt, - (void *)dacp); - osalDbgAssert(!b, "stream already allocated"); - - /* DMA settings depend on the chosed DAC mode.*/ - switch (dacp->config->datamode) { - /* Sets the DAC data register */ - case DAC_DHRM_12BIT_RIGHT: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12R1 + - dacp->params->dataoffset); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - break; - case DAC_DHRM_12BIT_LEFT: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12L1 + - dacp->params->dataoffset); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - break; - case DAC_DHRM_8BIT_RIGHT: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR8R1 + - dacp->params->dataoffset); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; - - /* In this mode the size of the buffer is halved because two samples - packed in a single dacsample_t element.*/ - n = (n + 1) / 2; - break; -#if STM32_DAC_DUAL_MODE == TRUE - case DAC_DHRM_12BIT_RIGHT_DUAL: - osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12RD); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; - n /= 2; - break; - case DAC_DHRM_12BIT_LEFT_DUAL: - osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12LD); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; - n /= 2; - break; - case DAC_DHRM_8BIT_RIGHT_DUAL: - osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); - - dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR8RD); - dmamode = dacp->params->dmamode | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - n /= 2; - break; -#endif - default: - osalDbgAssert(false, "unexpected DAC mode"); - return; - } - - dmaStreamSetMemory0(dacp->params->dma, dacp->samples); - dmaStreamSetTransactionSize(dacp->params->dma, n); - dmaStreamSetMode(dacp->params->dma, dmamode | - STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | - STM32_DMA_CR_HTIE | STM32_DMA_CR_TCIE); - dmaStreamEnable(dacp->params->dma); - - /* DAC configuration.*/ -#if STM32_DAC_DUAL_MODE == FALSE - cr = DAC_CR_DMAEN1 | (dacp->grpp->trigger << 3) | DAC_CR_TEN1 | DAC_CR_EN1; - dacp->params->dac->CR &= dacp->params->regmask; - dacp->params->dac->CR |= cr << dacp->params->regshift; -#else - dacp->params->dac->CR = 0; - cr = DAC_CR_DMAEN1 | (dacp->grpp->trigger << 3) | DAC_CR_TEN1 | DAC_CR_EN1 - | (dacp->grpp->trigger << 19) | DAC_CR_TEN2 | DAC_CR_EN2; - dacp->params->dac->CR = cr; -#endif -} - -/** - * @brief Stops an ongoing conversion. - * @details This function stops the currently ongoing conversion and returns - * the driver in the @p DAC_READY state. If there was no conversion - * being processed then the function does nothing. - * - * @param[in] dacp pointer to the @p DACDriver object - * - * @iclass - */ -void dac_lld_stop_conversion(DACDriver *dacp) { - - /* DMA channel disabled and released.*/ - dmaStreamDisable(dacp->params->dma); - dmaStreamRelease(dacp->params->dma); - -#if STM32_DAC_DUAL_MODE == FALSE - dacp->params->dac->CR &= dacp->params->regmask; - dacp->params->dac->CR |= DAC_CR_EN1 << dacp->params->regshift; -#else - if ((dacp->config->datamode == DAC_DHRM_12BIT_RIGHT_DUAL) || - (dacp->config->datamode == DAC_DHRM_12BIT_LEFT_DUAL) || - (dacp->config->datamode == DAC_DHRM_8BIT_RIGHT_DUAL)) { - dacp->params->dac->CR = DAC_CR_EN2 | DAC_CR_EN1; - } - else { - dacp->params->dac->CR = DAC_CR_EN1; - } -#endif -} - -#endif /* HAL_USE_DAC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/DACv1/dac_lld.h b/os/hal/ports/STM32/LLD/DACv1/dac_lld.h deleted file mode 100644 index d69e7d86f..000000000 --- a/os/hal/ports/STM32/LLD/DACv1/dac_lld.h +++ /dev/null @@ -1,465 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/DACv1/dac_lld.h - * @brief STM32 DAC subsystem low level driver header. - * - * @addtogroup DAC - * @{ - */ - -#ifndef _DAC_LLD_H_ -#define _DAC_LLD_H_ - -#if HAL_USE_DAC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name DAC trigger modes - * @{ - */ -#define DAC_TRG_MASK 7U -#define DAC_TRG(n) (n) -#define DAC_TRG_EXT 6U -#define DAC_TRG_SW 7U -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief Enables the DAC dual mode. - * @note In dual mode DAC second channels cannot be accessed individually. - */ -#if !defined(STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) -#define STM32_DAC_DUAL_MODE FALSE -#endif - -/** - * @brief DAC1 CH1 driver enable switch. - * @details If set to @p TRUE the support for DAC1 channel 1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC1_CH1) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC1_CH1 FALSE -#endif - -/** - * @brief DAC1 CH2 driver enable switch. - * @details If set to @p TRUE the support for DAC1 channel 2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC1_CH2) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC1_CH2 FALSE -#endif - -/** - * @brief DAC2 CH1 driver enable switch. - * @details If set to @p TRUE the support for DAC2 channel 1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC2_CH1) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC2_CH1 FALSE -#endif - -/** - * @brief DAC2 CH2 driver enable switch. - * @details If set to @p TRUE the support for DAC2 channel 2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_DAC_USE_DAC2_CH2) || defined(__DOXYGEN__) -#define STM32_DAC_USE_DAC2_CH2 FALSE -#endif - -/** - * @brief DAC1 CH1 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC1_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC1 CH2 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC1_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC2 CH1 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC2_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH1_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC2 CH2 interrupt priority level setting. - */ -#if !defined(STM32_DAC_DAC2_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH2_IRQ_PRIORITY 10 -#endif - -/** - * @brief DAC1 CH1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC1_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC1 CH2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC1_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC2 CH1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC2_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH1_DMA_PRIORITY 2 -#endif - -/** - * @brief DAC2 CH2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_DAC_DAC2_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_DAC_DAC2_CH2_DMA_PRIORITY 2 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_DAC_USE_DAC1_CH1 && !STM32_HAS_DAC1_CH1 -#error "DAC1 CH1 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && !STM32_HAS_DAC1_CH2 -#error "DAC1 CH2 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && !STM32_HAS_DAC2_CH1 -#error "DAC2 CH1 not present in the selected device" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && !STM32_HAS_DAC2_CH2 -#error "DAC2 CH2 not present in the selected device" -#endif - -#if (STM32_DAC_USE_DAC1_CH2 || STM32_DAC_USE_DAC2_CH2) && STM32_DAC_DUAL_MODE -#error "DACx CH2 cannot be used independently in dual mode" -#endif - -#if !STM32_DAC_USE_DAC1_CH1 && !STM32_DAC_USE_DAC1_CH2 && \ - !STM32_DAC_USE_DAC2_CH1 && !STM32_DAC_USE_DAC2_CH2 -#error "DAC driver activated but no DAC peripheral assigned" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_DAC_USE_DAC1_CH1 && !defined(STM32_DAC_DAC1_CH1_DMA_STREAM) -#error "DAC1 CH1 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && !defined(STM32_DAC_DAC1_CH2_DMA_STREAM) -#error "DAC1 CH2 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && !defined(STM32_DAC_DAC2_CH1_DMA_STREAM) -#error "DAC2 CH1 DMA stream not defined" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && !defined(STM32_DAC_DAC2_CH2_DMA_STREAM) -#error "DAC2 CH2 DMA stream not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_DAC_USE_DAC1_CH1 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH1_DMA_STREAM, STM32_DAC1_CH1_DMA_MSK) -#error "invalid DMA stream associated to DAC1 CH1" -#endif - -#if STM32_DAC_USE_DAC1_CH2 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH2_DMA_STREAM, STM32_DAC1_CH2_DMA_MSK) -#error "invalid DMA stream associated to DAC1 CH2" -#endif - -#if STM32_DAC_USE_DAC2_CH1 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH1_DMA_STREAM, STM32_DAC2_CH1_DMA_MSK) -#error "invalid DMA stream associated to DAC2 CH1" -#endif - -#if STM32_DAC_USE_DAC2_CH2 && \ - !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH2_DMA_STREAM, STM32_DAC2_CH2_DMA_MSK) -#error "invalid DMA stream associated to DAC2 CH2" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/** - * @brief Max DAC channels. - */ -#if STM32_DAC_DUAL_MODE == FALSE -#define DAC_MAX_CHANNELS 1 -#else -#define DAC_MAX_CHANNELS 2 -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a DAC channel index. - */ -typedef uint32_t dacchannel_t; - -/** - * @brief DAC channel parameters type. - */ -typedef struct { - /** - * @brief Pointer to the DAC registers block. - */ - DAC_TypeDef *dac; - /** - * @brief DAC data registers offset. - */ - uint32_t dataoffset; - /** - * @brief DAC CR register bit offset. - */ - uint32_t regshift; - /** - * @brief DAC CR register mask. - */ - uint32_t regmask; - /** - * @brief Associated DMA. - */ - const stm32_dma_stream_t *dma; - /** - * @brief Mode bits for the DMA. - */ - uint32_t dmamode; - /** - * @brief DMA channel IRQ priority. - */ - uint32_t dmairqprio; -} dacparams_t; - -/** - * @brief Type of a structure representing an DAC driver. - */ -typedef struct DACDriver DACDriver; - -/** - * @brief Type representing a DAC sample. - */ -typedef uint16_t dacsample_t; - -/** - * @brief Possible DAC failure causes. - * @note Error codes are architecture dependent and should not relied - * upon. - */ -typedef enum { - DAC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ - DAC_ERR_UNDERFLOW = 1 /**< DAC overflow condition. */ -} dacerror_t; - -/** - * @brief DAC notification callback type. - * - * @param[in] dacp pointer to the @p DACDriver object triggering the - * @param[in] buffer pointer to the next semi-buffer to be filled - * @param[in] n number of buffer rows available starting from @p buffer - * callback - */ -typedef void (*daccallback_t)(DACDriver *dacp, - const dacsample_t *buffer, - size_t n); - -/** - * @brief ADC error callback type. - * - * @param[in] dacp pointer to the @p DACDriver object triggering the - * callback - * @param[in] err ADC error code - */ -typedef void (*dacerrorcallback_t)(DACDriver *dacp, dacerror_t err); - -/** - * @brief Samples alignment and size mode. - */ -typedef enum { - DAC_DHRM_12BIT_RIGHT = 0, - DAC_DHRM_12BIT_LEFT = 1, - DAC_DHRM_8BIT_RIGHT = 2, -#if STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) - DAC_DHRM_12BIT_RIGHT_DUAL = 3, - DAC_DHRM_12BIT_LEFT_DUAL = 4, - DAC_DHRM_8BIT_RIGHT_DUAL = 5 -#endif -} dacdhrmode_t; - -/** - * @brief DAC Conversion group structure. - */ -typedef struct { - /** - * @brief Number of DAC channels. - */ - uint32_t num_channels; - /** - * @brief Operation complete callback or @p NULL. - */ - daccallback_t end_cb; - /** - * @brief Error handling callback or @p NULL. - */ - dacerrorcallback_t error_cb; - /* End of the mandatory fields.*/ - /** - * @brief DAC initialization data. - * @note This field contains the (not shifted) value to be put into the - * TSEL field of the DAC CR register during initialization. All - * other fields are handled internally. - */ - uint32_t trigger; -} DACConversionGroup; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /* End of the mandatory fields.*/ - /** - * @brief Initial output on DAC channels. - */ - dacsample_t init; - /** - * @brief DAC data holding register mode. - */ - dacdhrmode_t datamode; -} DACConfig; - -/** - * @brief Structure representing a DAC driver. - */ -struct DACDriver { - /** - * @brief Driver state. - */ - dacstate_t state; - /** - * @brief Conversion group. - */ - const DACConversionGroup *grpp; - /** - * @brief Samples buffer pointer. - */ - const dacsample_t *samples; - /** - * @brief Samples buffer size. - */ - uint16_t depth; - /** - * @brief Current configuration data. - */ - const DACConfig *config; -#if DAC_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif /* DAC_USE_WAIT */ -#if DAC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#endif /* DAC_USE_MUTUAL_EXCLUSION */ -#if defined(DAC_DRIVER_EXT_FIELDS) - DAC_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief DAC channel parameters. - */ - const dacparams_t *params; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_DAC_USE_DAC1_CH1 && !defined(__DOXYGEN__) -extern DACDriver DACD1; -#endif - -#if STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) -extern DACDriver DACD2; -#endif - -#if STM32_DAC_USE_DAC2_CH1 && !defined(__DOXYGEN__) -extern DACDriver DACD3; -#endif - -#if STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) -extern DACDriver DACD4; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void dac_lld_init(void); - void dac_lld_start(DACDriver *dacp); - void dac_lld_stop(DACDriver *dacp); - void dac_lld_put_channel(DACDriver *dacp, - dacchannel_t channel, - dacsample_t sample); - void dac_lld_start_conversion(DACDriver *dacp); - void dac_lld_stop_conversion(DACDriver *dacp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_DAC */ - -#endif /* _DAC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c new file mode 100644 index 000000000..aae757cc0 --- /dev/null +++ b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.c @@ -0,0 +1,515 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/DACv1/dac_lld.c + * @brief STM32 DAC subsystem low level driver source. + * + * @addtogroup DAC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_DAC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/* Because ST headers naming inconsistencies.*/ +#if !defined(DAC1) +#define DAC1 DAC +#endif + +#define DAC1_CH1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH1_DMA_STREAM, \ + STM32_DAC1_CH1_DMA_CHN) + +#define DAC1_CH2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC1_CH2_DMA_STREAM, \ + STM32_DAC1_CH2_DMA_CHN) + +#define DAC2_CH1_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH1_DMA_STREAM, \ + STM32_DAC2_CH1_DMA_CHN) + +#define DAC2_CH2_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_DAC_DAC2_CH2_DMA_STREAM, \ + STM32_DAC2_CH2_DMA_CHN) + +#define CHANNEL_DATA_OFFSET 3U + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief DAC1 CH1 driver identifier.*/ +#if STM32_DAC_USE_DAC1_CH1 || defined(__DOXYGEN__) +DACDriver DACD1; +#endif + +/** @brief DAC1 CH2 driver identifier.*/ +#if (STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +DACDriver DACD2; +#endif + +/** @brief DAC2 CH1 driver identifier.*/ +#if STM32_DAC_USE_DAC2_CH1 || defined(__DOXYGEN__) +DACDriver DACD3; +#endif + +/** @brief DAC2 CH2 driver identifier.*/ +#if (STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +DACDriver DACD4; +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +#if STM32_DAC_USE_DAC1_CH1 == TRUE +static const dacparams_t dma1_ch1_params = { + .dac = DAC1, + .dataoffset = 0U, + .regshift = 0U, + .regmask = 0xFFFF0000U, + .dma = STM32_DMA_STREAM(STM32_DAC_DAC1_CH1_DMA_STREAM), + .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC1_CH1_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC1_CH1_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC1_CH2 == TRUE +static const dacparams_t dma1_ch2_params = { + .dac = DAC1, + .dataoffset = CHANNEL_DATA_OFFSET, + .regshift = 16U, + .regmask = 0x0000FFFFU, + .dma = STM32_DMA_STREAM(STM32_DAC_DAC1_CH2_DMA_STREAM), + .dmamode = STM32_DMA_CR_CHSEL(DAC1_CH2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC1_CH2_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC1_CH2_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC2_CH1 == TRUE +static const dacparams_t dma2_ch1_params = { + .dac = DAC2, + .dataoffset = 0U, + .regshift = 0U, + .regmask = 0xFFFF0000U, + .dma = STM32_DMA_STREAM(STM32_DAC_DAC2_CH1_DMA_STREAM), + .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH1_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC2_CH1_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC2_CH1_IRQ_PRIORITY +}; +#endif + +#if STM32_DAC_USE_DAC2_CH2 == TRUE +static const dacparams_t dma1_ch2_params = { + .dac = DAC2, + .dataoffset = CHANNEL_DATA_OFFSET, + .regshift = 16U, + .regmask = 0x0000FFFFU, + .dma = STM32_DMA_STREAM(STM32_DAC_DAC2_CH2_DMA_STREAM), + .dmamode = STM32_DMA_CR_CHSEL(DAC2_CH2_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_DAC_DAC2_CH2_DMA_PRIORITY) | + STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE, + .dmairqprio = STM32_DAC_DAC2_CH2_IRQ_PRIORITY +}; +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Shared end/half-of-tx service routine. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void dac_lld_serve_tx_interrupt(DACDriver *dacp, uint32_t flags) { + + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + /* DMA errors handling.*/ + _dac_isr_error_code(dacp, DAC_ERR_DMAFAILURE); + } + else { + if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _dac_isr_half_code(dacp); + } + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _dac_isr_full_code(dacp); + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level DAC driver initialization. + * + * @notapi + */ +void dac_lld_init(void) { + +#if STM32_DAC_USE_DAC1_CH1 + dacObjectInit(&DACD1); + DACD1.params = &dma1_ch1_params; +#endif + +#if STM32_DAC_USE_DAC1_CH2 + dacObjectInit(&DACD2); + DACD2.params = &dma1_ch2_params; +#endif + +#if STM32_DAC_USE_DAC2_CH1 + dacObjectInit(&DACD3); + DACD3.params = &dma2_ch1_params; +#endif + +#if STM32_DAC_USE_DAC2_CH2 + dacObjectInit(&DACD4); + DACD4.params = &dma2_ch2_params; +#endif +} + +/** + * @brief Configures and activates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_start(DACDriver *dacp) { + + /* If the driver is in DAC_STOP state then a full initialization is + required.*/ + if (dacp->state == DAC_STOP) { + /* Enabling the clock source.*/ +#if STM32_DAC_USE_DAC1_CH1 + if (&DACD1 == dacp) { + rccEnableDAC1(false); + } +#endif + +#if STM32_DAC_USE_DAC1_CH2 + if (&DACD2 == dacp) { + rccEnableDAC1(false); + } +#endif + +#if STM32_DAC_USE_DAC2_CH1 + if (&DACD3 == dacp) { + rccEnableDAC2(false); + } +#endif + +#if STM32_DAC_USE_DAC2_CH2 + if (&DACD3 == dacp) { + rccEnableDAC2(false); + } +#endif + + /* Enabling DAC in SW triggering mode initially, initializing data to + zero.*/ +#if STM32_DAC_DUAL_MODE == FALSE + dacp->params->dac->CR &= dacp->params->regmask; + dacp->params->dac->CR |= DAC_CR_EN1 << dacp->params->regshift; + dac_lld_put_channel(dacp, 0U, dacp->config->init); +#else + if ((dacp->config->datamode == DAC_DHRM_12BIT_RIGHT_DUAL) || + (dacp->config->datamode == DAC_DHRM_12BIT_LEFT_DUAL) || + (dacp->config->datamode == DAC_DHRM_8BIT_RIGHT_DUAL)) { + dacp->params->dac->CR = DAC_CR_EN2 | DAC_CR_EN1; + dac_lld_put_channel(dacp, 1U, dacp->config->init); + } + else { + dacp->params->dac->CR = DAC_CR_EN1; + } + dac_lld_put_channel(dacp, 0U, dacp->config->init); +#endif + } +} + +/** + * @brief Deactivates the DAC peripheral. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_stop(DACDriver *dacp) { + + /* If in ready state then disables the DAC clock.*/ + if (dacp->state == DAC_READY) { + + /* Disabling DAC.*/ + dacp->params->dac->CR &= dacp->params->regmask; + +#if STM32_DAC_USE_DAC1_CH1 + if (&DACD1 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { + rccDisableDAC1(false); + } + } +#endif + +#if STM32_DAC_USE_DAC1_CH2 + if (&DACD2 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { + rccDisableDAC1(false); + } + } +#endif + +#if STM32_DAC_USE_DAC2_CH1 + if (&DACD3 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN2) == 0U) { + rccDisableDAC2(false); + } + } +#endif + +#if STM32_DAC_USE_DAC2_CH2 + if (&DACD4 == dacp) { + if ((dacp->params->dac->CR & DAC_CR_EN1) == 0U) { + rccDisableDAC2(false); + } + } +#endif + } +} + +/** + * @brief Outputs a value directly on a DAC channel. + * + * @param[in] dacp pointer to the @p DACDriver object + * @param[in] channel DAC channel number + * @param[in] sample value to be output + * + * @api + */ +void dac_lld_put_channel(DACDriver *dacp, + dacchannel_t channel, + dacsample_t sample) { + + switch (dacp->config->datamode) { + case DAC_DHRM_12BIT_RIGHT: +#if STM32_DAC_DUAL_MODE + case DAC_DHRM_12BIT_RIGHT_DUAL: +#endif + if (channel == 0U) { + dacp->params->dac->DHR12R1 = (uint32_t)sample; + } + else { + dacp->params->dac->DHR12R2 = (uint32_t)sample; + } + break; + case DAC_DHRM_12BIT_LEFT: +#if STM32_DAC_DUAL_MODE + case DAC_DHRM_12BIT_LEFT_DUAL: +#endif + if (channel == 0U) { + dacp->params->dac->DHR12L1 = (uint32_t)sample; + } + else { + dacp->params->dac->DHR12L2 = (uint32_t)sample; + } + break; + case DAC_DHRM_8BIT_RIGHT: +#if STM32_DAC_DUAL_MODE + case DAC_DHRM_8BIT_RIGHT_DUAL: +#endif + if (channel == 0U) { + dacp->params->dac->DHR8R1 = (uint32_t)sample; + } + else { + dacp->params->dac->DHR8R2 = (uint32_t)sample; + } + break; + default: + osalDbgAssert(false, "unexpected DAC mode"); + break; + } +} + +/** + * @brief Starts a DAC conversion. + * @details Starts an asynchronous conversion operation. + * @note In @p DAC_DHRM_8BIT_RIGHT mode the parameters passed to the + * callback are wrong because two samples are packed in a single + * dacsample_t element. This will not be corrected, do not rely + * on those parameters. + * @note In @p DAC_DHRM_8BIT_RIGHT_DUAL mode two samples are treated + * as a single 16 bits sample and packed into a single dacsample_t + * element. The num_channels must be set to one in the group + * conversion configuration structure. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @notapi + */ +void dac_lld_start_conversion(DACDriver *dacp) { + uint32_t n, cr, dmamode; + + /* Number of DMA operations per buffer.*/ + n = dacp->depth * dacp->grpp->num_channels; + + /* Allocating the DMA channel.*/ + bool b = dmaStreamAllocate(dacp->params->dma, dacp->params->dmairqprio, + (stm32_dmaisr_t)dac_lld_serve_tx_interrupt, + (void *)dacp); + osalDbgAssert(!b, "stream already allocated"); + + /* DMA settings depend on the chosed DAC mode.*/ + switch (dacp->config->datamode) { + /* Sets the DAC data register */ + case DAC_DHRM_12BIT_RIGHT: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12R1 + + dacp->params->dataoffset); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + break; + case DAC_DHRM_12BIT_LEFT: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12L1 + + dacp->params->dataoffset); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + break; + case DAC_DHRM_8BIT_RIGHT: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR8R1 + + dacp->params->dataoffset); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; + + /* In this mode the size of the buffer is halved because two samples + packed in a single dacsample_t element.*/ + n = (n + 1) / 2; + break; +#if STM32_DAC_DUAL_MODE == TRUE + case DAC_DHRM_12BIT_RIGHT_DUAL: + osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12RD); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; + n /= 2; + break; + case DAC_DHRM_12BIT_LEFT_DUAL: + osalDbgAssert(dacp->grpp->num_channels == 2, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR12LD); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD; + n /= 2; + break; + case DAC_DHRM_8BIT_RIGHT_DUAL: + osalDbgAssert(dacp->grpp->num_channels == 1, "invalid number of channels"); + + dmaStreamSetPeripheral(dacp->params->dma, &dacp->params->dac->DHR8RD); + dmamode = dacp->params->dmamode | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + n /= 2; + break; +#endif + default: + osalDbgAssert(false, "unexpected DAC mode"); + return; + } + + dmaStreamSetMemory0(dacp->params->dma, dacp->samples); + dmaStreamSetTransactionSize(dacp->params->dma, n); + dmaStreamSetMode(dacp->params->dma, dmamode | + STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE | + STM32_DMA_CR_HTIE | STM32_DMA_CR_TCIE); + dmaStreamEnable(dacp->params->dma); + + /* DAC configuration.*/ +#if STM32_DAC_DUAL_MODE == FALSE + cr = DAC_CR_DMAEN1 | (dacp->grpp->trigger << 3) | DAC_CR_TEN1 | DAC_CR_EN1; + dacp->params->dac->CR &= dacp->params->regmask; + dacp->params->dac->CR |= cr << dacp->params->regshift; +#else + dacp->params->dac->CR = 0; + cr = DAC_CR_DMAEN1 | (dacp->grpp->trigger << 3) | DAC_CR_TEN1 | DAC_CR_EN1 + | (dacp->grpp->trigger << 19) | DAC_CR_TEN2 | DAC_CR_EN2; + dacp->params->dac->CR = cr; +#endif +} + +/** + * @brief Stops an ongoing conversion. + * @details This function stops the currently ongoing conversion and returns + * the driver in the @p DAC_READY state. If there was no conversion + * being processed then the function does nothing. + * + * @param[in] dacp pointer to the @p DACDriver object + * + * @iclass + */ +void dac_lld_stop_conversion(DACDriver *dacp) { + + /* DMA channel disabled and released.*/ + dmaStreamDisable(dacp->params->dma); + dmaStreamRelease(dacp->params->dma); + +#if STM32_DAC_DUAL_MODE == FALSE + dacp->params->dac->CR &= dacp->params->regmask; + dacp->params->dac->CR |= DAC_CR_EN1 << dacp->params->regshift; +#else + if ((dacp->config->datamode == DAC_DHRM_12BIT_RIGHT_DUAL) || + (dacp->config->datamode == DAC_DHRM_12BIT_LEFT_DUAL) || + (dacp->config->datamode == DAC_DHRM_8BIT_RIGHT_DUAL)) { + dacp->params->dac->CR = DAC_CR_EN2 | DAC_CR_EN1; + } + else { + dacp->params->dac->CR = DAC_CR_EN1; + } +#endif +} + +#endif /* HAL_USE_DAC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h new file mode 100644 index 000000000..d69e7d86f --- /dev/null +++ b/os/hal/ports/STM32/LLD/DACv1/hal_dac_lld.h @@ -0,0 +1,465 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/DACv1/dac_lld.h + * @brief STM32 DAC subsystem low level driver header. + * + * @addtogroup DAC + * @{ + */ + +#ifndef _DAC_LLD_H_ +#define _DAC_LLD_H_ + +#if HAL_USE_DAC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name DAC trigger modes + * @{ + */ +#define DAC_TRG_MASK 7U +#define DAC_TRG(n) (n) +#define DAC_TRG_EXT 6U +#define DAC_TRG_SW 7U +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Enables the DAC dual mode. + * @note In dual mode DAC second channels cannot be accessed individually. + */ +#if !defined(STM32_DAC_DUAL_MODE) || defined(__DOXYGEN__) +#define STM32_DAC_DUAL_MODE FALSE +#endif + +/** + * @brief DAC1 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC1 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC1_CH1) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC1_CH1 FALSE +#endif + +/** + * @brief DAC1 CH2 driver enable switch. + * @details If set to @p TRUE the support for DAC1 channel 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC1_CH2) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC1_CH2 FALSE +#endif + +/** + * @brief DAC2 CH1 driver enable switch. + * @details If set to @p TRUE the support for DAC2 channel 1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC2_CH1) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC2_CH1 FALSE +#endif + +/** + * @brief DAC2 CH2 driver enable switch. + * @details If set to @p TRUE the support for DAC2 channel 2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_DAC_USE_DAC2_CH2) || defined(__DOXYGEN__) +#define STM32_DAC_USE_DAC2_CH2 FALSE +#endif + +/** + * @brief DAC1 CH1 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC1_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC1 CH2 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC1_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC2 CH1 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC2_CH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH1_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC2 CH2 interrupt priority level setting. + */ +#if !defined(STM32_DAC_DAC2_CH2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH2_IRQ_PRIORITY 10 +#endif + +/** + * @brief DAC1 CH1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC1_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC1 CH2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC1_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC2 CH1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC2_CH1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH1_DMA_PRIORITY 2 +#endif + +/** + * @brief DAC2 CH2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_DAC_DAC2_CH2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_DAC_DAC2_CH2_DMA_PRIORITY 2 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_DAC_USE_DAC1_CH1 && !STM32_HAS_DAC1_CH1 +#error "DAC1 CH1 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && !STM32_HAS_DAC1_CH2 +#error "DAC1 CH2 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && !STM32_HAS_DAC2_CH1 +#error "DAC2 CH1 not present in the selected device" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && !STM32_HAS_DAC2_CH2 +#error "DAC2 CH2 not present in the selected device" +#endif + +#if (STM32_DAC_USE_DAC1_CH2 || STM32_DAC_USE_DAC2_CH2) && STM32_DAC_DUAL_MODE +#error "DACx CH2 cannot be used independently in dual mode" +#endif + +#if !STM32_DAC_USE_DAC1_CH1 && !STM32_DAC_USE_DAC1_CH2 && \ + !STM32_DAC_USE_DAC2_CH1 && !STM32_DAC_USE_DAC2_CH2 +#error "DAC driver activated but no DAC peripheral assigned" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_DAC_USE_DAC1_CH1 && !defined(STM32_DAC_DAC1_CH1_DMA_STREAM) +#error "DAC1 CH1 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && !defined(STM32_DAC_DAC1_CH2_DMA_STREAM) +#error "DAC1 CH2 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && !defined(STM32_DAC_DAC2_CH1_DMA_STREAM) +#error "DAC2 CH1 DMA stream not defined" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && !defined(STM32_DAC_DAC2_CH2_DMA_STREAM) +#error "DAC2 CH2 DMA stream not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_DAC_USE_DAC1_CH1 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH1_DMA_STREAM, STM32_DAC1_CH1_DMA_MSK) +#error "invalid DMA stream associated to DAC1 CH1" +#endif + +#if STM32_DAC_USE_DAC1_CH2 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC1_CH2_DMA_STREAM, STM32_DAC1_CH2_DMA_MSK) +#error "invalid DMA stream associated to DAC1 CH2" +#endif + +#if STM32_DAC_USE_DAC2_CH1 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH1_DMA_STREAM, STM32_DAC2_CH1_DMA_MSK) +#error "invalid DMA stream associated to DAC2 CH1" +#endif + +#if STM32_DAC_USE_DAC2_CH2 && \ + !STM32_DMA_IS_VALID_ID(STM32_DAC_DAC2_CH2_DMA_STREAM, STM32_DAC2_CH2_DMA_MSK) +#error "invalid DMA stream associated to DAC2 CH2" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/** + * @brief Max DAC channels. + */ +#if STM32_DAC_DUAL_MODE == FALSE +#define DAC_MAX_CHANNELS 1 +#else +#define DAC_MAX_CHANNELS 2 +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a DAC channel index. + */ +typedef uint32_t dacchannel_t; + +/** + * @brief DAC channel parameters type. + */ +typedef struct { + /** + * @brief Pointer to the DAC registers block. + */ + DAC_TypeDef *dac; + /** + * @brief DAC data registers offset. + */ + uint32_t dataoffset; + /** + * @brief DAC CR register bit offset. + */ + uint32_t regshift; + /** + * @brief DAC CR register mask. + */ + uint32_t regmask; + /** + * @brief Associated DMA. + */ + const stm32_dma_stream_t *dma; + /** + * @brief Mode bits for the DMA. + */ + uint32_t dmamode; + /** + * @brief DMA channel IRQ priority. + */ + uint32_t dmairqprio; +} dacparams_t; + +/** + * @brief Type of a structure representing an DAC driver. + */ +typedef struct DACDriver DACDriver; + +/** + * @brief Type representing a DAC sample. + */ +typedef uint16_t dacsample_t; + +/** + * @brief Possible DAC failure causes. + * @note Error codes are architecture dependent and should not relied + * upon. + */ +typedef enum { + DAC_ERR_DMAFAILURE = 0, /**< DMA operations failure. */ + DAC_ERR_UNDERFLOW = 1 /**< DAC overflow condition. */ +} dacerror_t; + +/** + * @brief DAC notification callback type. + * + * @param[in] dacp pointer to the @p DACDriver object triggering the + * @param[in] buffer pointer to the next semi-buffer to be filled + * @param[in] n number of buffer rows available starting from @p buffer + * callback + */ +typedef void (*daccallback_t)(DACDriver *dacp, + const dacsample_t *buffer, + size_t n); + +/** + * @brief ADC error callback type. + * + * @param[in] dacp pointer to the @p DACDriver object triggering the + * callback + * @param[in] err ADC error code + */ +typedef void (*dacerrorcallback_t)(DACDriver *dacp, dacerror_t err); + +/** + * @brief Samples alignment and size mode. + */ +typedef enum { + DAC_DHRM_12BIT_RIGHT = 0, + DAC_DHRM_12BIT_LEFT = 1, + DAC_DHRM_8BIT_RIGHT = 2, +#if STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) + DAC_DHRM_12BIT_RIGHT_DUAL = 3, + DAC_DHRM_12BIT_LEFT_DUAL = 4, + DAC_DHRM_8BIT_RIGHT_DUAL = 5 +#endif +} dacdhrmode_t; + +/** + * @brief DAC Conversion group structure. + */ +typedef struct { + /** + * @brief Number of DAC channels. + */ + uint32_t num_channels; + /** + * @brief Operation complete callback or @p NULL. + */ + daccallback_t end_cb; + /** + * @brief Error handling callback or @p NULL. + */ + dacerrorcallback_t error_cb; + /* End of the mandatory fields.*/ + /** + * @brief DAC initialization data. + * @note This field contains the (not shifted) value to be put into the + * TSEL field of the DAC CR register during initialization. All + * other fields are handled internally. + */ + uint32_t trigger; +} DACConversionGroup; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /* End of the mandatory fields.*/ + /** + * @brief Initial output on DAC channels. + */ + dacsample_t init; + /** + * @brief DAC data holding register mode. + */ + dacdhrmode_t datamode; +} DACConfig; + +/** + * @brief Structure representing a DAC driver. + */ +struct DACDriver { + /** + * @brief Driver state. + */ + dacstate_t state; + /** + * @brief Conversion group. + */ + const DACConversionGroup *grpp; + /** + * @brief Samples buffer pointer. + */ + const dacsample_t *samples; + /** + * @brief Samples buffer size. + */ + uint16_t depth; + /** + * @brief Current configuration data. + */ + const DACConfig *config; +#if DAC_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* DAC_USE_WAIT */ +#if DAC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* DAC_USE_MUTUAL_EXCLUSION */ +#if defined(DAC_DRIVER_EXT_FIELDS) + DAC_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief DAC channel parameters. + */ + const dacparams_t *params; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_DAC_USE_DAC1_CH1 && !defined(__DOXYGEN__) +extern DACDriver DACD1; +#endif + +#if STM32_DAC_USE_DAC1_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) +extern DACDriver DACD2; +#endif + +#if STM32_DAC_USE_DAC2_CH1 && !defined(__DOXYGEN__) +extern DACDriver DACD3; +#endif + +#if STM32_DAC_USE_DAC2_CH2 && !STM32_DAC_DUAL_MODE && !defined(__DOXYGEN__) +extern DACDriver DACD4; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void dac_lld_init(void); + void dac_lld_start(DACDriver *dacp); + void dac_lld_stop(DACDriver *dacp); + void dac_lld_put_channel(DACDriver *dacp, + dacchannel_t channel, + dacsample_t sample); + void dac_lld_start_conversion(DACDriver *dacp); + void dac_lld_stop_conversion(DACDriver *dacp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_DAC */ + +#endif /* _DAC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/EXTIv1/ext_lld.c b/os/hal/ports/STM32/LLD/EXTIv1/ext_lld.c deleted file mode 100644 index e68b4de3f..000000000 --- a/os/hal/ports/STM32/LLD/EXTIv1/ext_lld.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/EXTIv1/ext_lld.c - * @brief STM32 EXT subsystem low level driver source. - * - * @addtogroup EXT - * @{ - */ - -#include "hal.h" - -#if HAL_USE_EXT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/* Handling a difference in ST headers.*/ -#if defined(STM32L4XX) -#define EMR EMR1 -#define IMR IMR1 -#define PR PR1 -#define RTSR RTSR1 -#define FTSR FTSR1 -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief EXTD1 driver identifier. - */ -EXTDriver EXTD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level EXT driver initialization. - * - * @notapi - */ -void ext_lld_init(void) { - - /* Driver initialization.*/ - extObjectInit(&EXTD1); -} - -/** - * @brief Configures and activates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_start(EXTDriver *extp) { - expchannel_t line; - - if (extp->state == EXT_STOP) - ext_lld_exti_irq_enable(); - - /* Configuration of automatic channels.*/ - for (line = 0; line < EXT_MAX_CHANNELS; line++) - if (extp->config->channels[line].mode & EXT_CH_MODE_AUTOSTART) - ext_lld_channel_enable(extp, line); - else - ext_lld_channel_disable(extp, line); -} - -/** - * @brief Deactivates the EXT peripheral. - * - * @param[in] extp pointer to the @p EXTDriver object - * - * @notapi - */ -void ext_lld_stop(EXTDriver *extp) { - - if (extp->state == EXT_ACTIVE) - ext_lld_exti_irq_disable(); - - EXTI->EMR = 0; - EXTI->IMR = STM32_EXTI_IMR_MASK; - EXTI->PR = ~STM32_EXTI_IMR_MASK; -#if STM32_EXTI_NUM_LINES > 32 - EXTI->IMR2 = STM32_EXTI_IMR2_MASK; - EXTI->PR2 = ~STM32_EXTI_IMR2_MASK; -#endif -} - -/** - * @brief Enables an EXT channel. - * - * @param[in] extp pointer to the @p EXTDriver object - * @param[in] channel channel to be enabled - * - * @notapi - */ -void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { - uint32_t cmask = (1 << (channel & 0x1F)); - - /* Setting the associated GPIO for external channels.*/ - if (channel < 16) { - uint32_t n = channel >> 2; - uint32_t mask = ~(0xF << ((channel & 3) * 4)); - uint32_t port = ((extp->config->channels[channel].mode & - EXT_MODE_GPIO_MASK) >> - EXT_MODE_GPIO_OFF) << ((channel & 3) * 4); - -#if defined(STM32F1XX) - AFIO->EXTICR[n] = (AFIO->EXTICR[n] & mask) | port; -#else /* !defined(STM32F1XX) */ - SYSCFG->EXTICR[n] = (SYSCFG->EXTICR[n] & mask) | port; -#endif /* !defined(STM32F1XX) */ - } - -#if STM32_EXTI_NUM_LINES > 32 - if (channel < 32) { -#endif - /* Masked out lines must not be touched by this driver.*/ - if ((cmask & STM32_EXTI_IMR_MASK) != 0U) { - return; - } - - /* Programming edge registers.*/ - if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE) - EXTI->RTSR |= cmask; - else - EXTI->RTSR &= ~cmask; - if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) - EXTI->FTSR |= cmask; - else - EXTI->FTSR &= ~cmask; - - /* Programming interrupt and event registers.*/ - if (extp->config->channels[channel].cb != NULL) { - EXTI->IMR |= cmask; - EXTI->EMR &= ~cmask; - } - else { - EXTI->EMR |= cmask; - EXTI->IMR &= ~cmask; - } -#if STM32_EXTI_NUM_LINES > 32 - } - else { - /* Masked out lines must not be touched by this driver.*/ - if ((cmask & STM32_EXTI_IMR2_MASK) != 0U) { - return; - } - - /* Programming edge registers.*/ - if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE) - EXTI->RTSR2 |= cmask; - else - EXTI->RTSR2 &= ~cmask; - if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) - EXTI->FTSR2 |= cmask; - else - EXTI->FTSR2 &= ~cmask; - - /* Programming interrupt and event registers.*/ - if (extp->config->channels[channel].cb != NULL) { - EXTI->IMR2 |= cmask; - EXTI->EMR2 &= ~cmask; - } - else { - EXTI->EMR2 |= cmask; - EXTI->IMR2 &= ~cmask; - } - } -#endif -} - -/** - * @brief Disables an EXT channel. - * - * @param[in] extp pointer to the @p EXTDriver object - * @param[in] channel channel to be disabled - * - * @notapi - */ -void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { - uint32_t cmask = (1 << (channel & 0x1F)); - - (void)extp; - -#if STM32_EXTI_NUM_LINES > 32 - if (channel < 32) { -#endif - EXTI->IMR &= ~cmask; - EXTI->EMR &= ~cmask; - EXTI->RTSR &= ~cmask; - EXTI->FTSR &= ~cmask; - EXTI->PR = cmask; -#if STM32_EXTI_NUM_LINES > 32 - } - else { - EXTI->IMR2 &= ~cmask; - EXTI->EMR2 &= ~cmask; - EXTI->RTSR2 &= ~cmask; - EXTI->FTSR2 &= ~cmask; - EXTI->PR2 = cmask; - } -#endif -} - -#endif /* HAL_USE_EXT */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/EXTIv1/ext_lld.h b/os/hal/ports/STM32/LLD/EXTIv1/ext_lld.h deleted file mode 100644 index 95bb3982e..000000000 --- a/os/hal/ports/STM32/LLD/EXTIv1/ext_lld.h +++ /dev/null @@ -1,155 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/EXTIv1/ext_lld.h - * @brief STM32 EXT subsystem low level driver header. - * - * @addtogroup EXT - * @{ - */ - -#ifndef _EXT_LLD_H_ -#define _EXT_LLD_H_ - -#if HAL_USE_EXT || defined(__DOXYGEN__) - -#include "ext_lld_isr.h" - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Available number of EXT channels. - */ -#define EXT_MAX_CHANNELS STM32_EXTI_NUM_LINES - -/** - * @name STM32-specific EXT channel modes - * @{ - */ -#define EXT_MODE_GPIO_MASK 0xF0 /**< @brief Port field mask. */ -#define EXT_MODE_GPIO_OFF 4 /**< @brief Port field offset. */ -#define EXT_MODE_GPIOA 0x00 /**< @brief GPIOA identifier. */ -#define EXT_MODE_GPIOB 0x10 /**< @brief GPIOB identifier. */ -#define EXT_MODE_GPIOC 0x20 /**< @brief GPIOC identifier. */ -#define EXT_MODE_GPIOD 0x30 /**< @brief GPIOD identifier. */ -#define EXT_MODE_GPIOE 0x40 /**< @brief GPIOE identifier. */ -#define EXT_MODE_GPIOF 0x50 /**< @brief GPIOF identifier. */ -#define EXT_MODE_GPIOG 0x60 /**< @brief GPIOG identifier. */ -#define EXT_MODE_GPIOH 0x70 /**< @brief GPIOH identifier. */ -#define EXT_MODE_GPIOI 0x80 /**< @brief GPIOI identifier. */ -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief EXT channel identifier. - */ -typedef uint32_t expchannel_t; - -/** - * @brief Type of an EXT generic notification callback. - * - * @param[in] extp pointer to the @p EXPDriver object triggering the - * callback - */ -typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); - -/** - * @brief Channel configuration structure. - */ -typedef struct { - /** - * @brief Channel mode. - */ - uint32_t mode; - /** - * @brief Channel callback. - * @details In the STM32 implementation a @p NULL callback pointer is - * valid and configures the channel as an event sources instead - * of an interrupt source. - */ - extcallback_t cb; -} EXTChannelConfig; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Channel configurations. - */ - EXTChannelConfig channels[EXT_MAX_CHANNELS]; - /* End of the mandatory fields.*/ -} EXTConfig; - -/** - * @brief Structure representing an EXT driver. - */ -struct EXTDriver { - /** - * @brief Driver state. - */ - extstate_t state; - /** - * @brief Current configuration data. - */ - const EXTConfig *config; - /* End of the mandatory fields.*/ -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern EXTDriver EXTD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void ext_lld_init(void); - void ext_lld_start(EXTDriver *extp); - void ext_lld_stop(EXTDriver *extp); - void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); - void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_EXT */ - -#endif /* _EXT_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.c b/os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.c new file mode 100644 index 000000000..e68b4de3f --- /dev/null +++ b/os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.c @@ -0,0 +1,239 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/EXTIv1/ext_lld.c + * @brief STM32 EXT subsystem low level driver source. + * + * @addtogroup EXT + * @{ + */ + +#include "hal.h" + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/* Handling a difference in ST headers.*/ +#if defined(STM32L4XX) +#define EMR EMR1 +#define IMR IMR1 +#define PR PR1 +#define RTSR RTSR1 +#define FTSR FTSR1 +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief EXTD1 driver identifier. + */ +EXTDriver EXTD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level EXT driver initialization. + * + * @notapi + */ +void ext_lld_init(void) { + + /* Driver initialization.*/ + extObjectInit(&EXTD1); +} + +/** + * @brief Configures and activates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_start(EXTDriver *extp) { + expchannel_t line; + + if (extp->state == EXT_STOP) + ext_lld_exti_irq_enable(); + + /* Configuration of automatic channels.*/ + for (line = 0; line < EXT_MAX_CHANNELS; line++) + if (extp->config->channels[line].mode & EXT_CH_MODE_AUTOSTART) + ext_lld_channel_enable(extp, line); + else + ext_lld_channel_disable(extp, line); +} + +/** + * @brief Deactivates the EXT peripheral. + * + * @param[in] extp pointer to the @p EXTDriver object + * + * @notapi + */ +void ext_lld_stop(EXTDriver *extp) { + + if (extp->state == EXT_ACTIVE) + ext_lld_exti_irq_disable(); + + EXTI->EMR = 0; + EXTI->IMR = STM32_EXTI_IMR_MASK; + EXTI->PR = ~STM32_EXTI_IMR_MASK; +#if STM32_EXTI_NUM_LINES > 32 + EXTI->IMR2 = STM32_EXTI_IMR2_MASK; + EXTI->PR2 = ~STM32_EXTI_IMR2_MASK; +#endif +} + +/** + * @brief Enables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be enabled + * + * @notapi + */ +void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) { + uint32_t cmask = (1 << (channel & 0x1F)); + + /* Setting the associated GPIO for external channels.*/ + if (channel < 16) { + uint32_t n = channel >> 2; + uint32_t mask = ~(0xF << ((channel & 3) * 4)); + uint32_t port = ((extp->config->channels[channel].mode & + EXT_MODE_GPIO_MASK) >> + EXT_MODE_GPIO_OFF) << ((channel & 3) * 4); + +#if defined(STM32F1XX) + AFIO->EXTICR[n] = (AFIO->EXTICR[n] & mask) | port; +#else /* !defined(STM32F1XX) */ + SYSCFG->EXTICR[n] = (SYSCFG->EXTICR[n] & mask) | port; +#endif /* !defined(STM32F1XX) */ + } + +#if STM32_EXTI_NUM_LINES > 32 + if (channel < 32) { +#endif + /* Masked out lines must not be touched by this driver.*/ + if ((cmask & STM32_EXTI_IMR_MASK) != 0U) { + return; + } + + /* Programming edge registers.*/ + if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE) + EXTI->RTSR |= cmask; + else + EXTI->RTSR &= ~cmask; + if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) + EXTI->FTSR |= cmask; + else + EXTI->FTSR &= ~cmask; + + /* Programming interrupt and event registers.*/ + if (extp->config->channels[channel].cb != NULL) { + EXTI->IMR |= cmask; + EXTI->EMR &= ~cmask; + } + else { + EXTI->EMR |= cmask; + EXTI->IMR &= ~cmask; + } +#if STM32_EXTI_NUM_LINES > 32 + } + else { + /* Masked out lines must not be touched by this driver.*/ + if ((cmask & STM32_EXTI_IMR2_MASK) != 0U) { + return; + } + + /* Programming edge registers.*/ + if (extp->config->channels[channel].mode & EXT_CH_MODE_RISING_EDGE) + EXTI->RTSR2 |= cmask; + else + EXTI->RTSR2 &= ~cmask; + if (extp->config->channels[channel].mode & EXT_CH_MODE_FALLING_EDGE) + EXTI->FTSR2 |= cmask; + else + EXTI->FTSR2 &= ~cmask; + + /* Programming interrupt and event registers.*/ + if (extp->config->channels[channel].cb != NULL) { + EXTI->IMR2 |= cmask; + EXTI->EMR2 &= ~cmask; + } + else { + EXTI->EMR2 |= cmask; + EXTI->IMR2 &= ~cmask; + } + } +#endif +} + +/** + * @brief Disables an EXT channel. + * + * @param[in] extp pointer to the @p EXTDriver object + * @param[in] channel channel to be disabled + * + * @notapi + */ +void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) { + uint32_t cmask = (1 << (channel & 0x1F)); + + (void)extp; + +#if STM32_EXTI_NUM_LINES > 32 + if (channel < 32) { +#endif + EXTI->IMR &= ~cmask; + EXTI->EMR &= ~cmask; + EXTI->RTSR &= ~cmask; + EXTI->FTSR &= ~cmask; + EXTI->PR = cmask; +#if STM32_EXTI_NUM_LINES > 32 + } + else { + EXTI->IMR2 &= ~cmask; + EXTI->EMR2 &= ~cmask; + EXTI->RTSR2 &= ~cmask; + EXTI->FTSR2 &= ~cmask; + EXTI->PR2 = cmask; + } +#endif +} + +#endif /* HAL_USE_EXT */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.h b/os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.h new file mode 100644 index 000000000..569b48b3f --- /dev/null +++ b/os/hal/ports/STM32/LLD/EXTIv1/hal_ext_lld.h @@ -0,0 +1,155 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/EXTIv1/ext_lld.h + * @brief STM32 EXT subsystem low level driver header. + * + * @addtogroup EXT + * @{ + */ + +#ifndef _EXT_LLD_H_ +#define _EXT_LLD_H_ + +#if HAL_USE_EXT || defined(__DOXYGEN__) + +#include "hal_ext_lld_isr.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Available number of EXT channels. + */ +#define EXT_MAX_CHANNELS STM32_EXTI_NUM_LINES + +/** + * @name STM32-specific EXT channel modes + * @{ + */ +#define EXT_MODE_GPIO_MASK 0xF0 /**< @brief Port field mask. */ +#define EXT_MODE_GPIO_OFF 4 /**< @brief Port field offset. */ +#define EXT_MODE_GPIOA 0x00 /**< @brief GPIOA identifier. */ +#define EXT_MODE_GPIOB 0x10 /**< @brief GPIOB identifier. */ +#define EXT_MODE_GPIOC 0x20 /**< @brief GPIOC identifier. */ +#define EXT_MODE_GPIOD 0x30 /**< @brief GPIOD identifier. */ +#define EXT_MODE_GPIOE 0x40 /**< @brief GPIOE identifier. */ +#define EXT_MODE_GPIOF 0x50 /**< @brief GPIOF identifier. */ +#define EXT_MODE_GPIOG 0x60 /**< @brief GPIOG identifier. */ +#define EXT_MODE_GPIOH 0x70 /**< @brief GPIOH identifier. */ +#define EXT_MODE_GPIOI 0x80 /**< @brief GPIOI identifier. */ +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief EXT channel identifier. + */ +typedef uint32_t expchannel_t; + +/** + * @brief Type of an EXT generic notification callback. + * + * @param[in] extp pointer to the @p EXPDriver object triggering the + * callback + */ +typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel); + +/** + * @brief Channel configuration structure. + */ +typedef struct { + /** + * @brief Channel mode. + */ + uint32_t mode; + /** + * @brief Channel callback. + * @details In the STM32 implementation a @p NULL callback pointer is + * valid and configures the channel as an event sources instead + * of an interrupt source. + */ + extcallback_t cb; +} EXTChannelConfig; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Channel configurations. + */ + EXTChannelConfig channels[EXT_MAX_CHANNELS]; + /* End of the mandatory fields.*/ +} EXTConfig; + +/** + * @brief Structure representing an EXT driver. + */ +struct EXTDriver { + /** + * @brief Driver state. + */ + extstate_t state; + /** + * @brief Current configuration data. + */ + const EXTConfig *config; + /* End of the mandatory fields.*/ +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern EXTDriver EXTD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void ext_lld_init(void); + void ext_lld_start(EXTDriver *extp); + void ext_lld_stop(EXTDriver *extp); + void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel); + void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_EXT */ + +#endif /* _EXT_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.c b/os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.c new file mode 100644 index 000000000..f1edb10b5 --- /dev/null +++ b/os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.c @@ -0,0 +1,183 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/GPIOv1/pal_lld.c + * @brief STM32 PAL low level driver code. + * + * @addtogroup PAL + * @{ + */ + +#include "hal.h" + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if STM32_HAS_GPIOG +#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \ + RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \ + RCC_APB2ENR_IOPEEN | RCC_APB2ENR_IOPFEN | \ + RCC_APB2ENR_IOPGEN | RCC_APB2ENR_AFIOEN) +#elif STM32_HAS_GPIOE +#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \ + RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \ + RCC_APB2ENR_IOPEEN | RCC_APB2ENR_AFIOEN) +#else +#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \ + RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \ + RCC_APB2ENR_AFIOEN) +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief STM32 I/O ports configuration. + * @details Ports A-D(E, F, G) clocks enabled, AFIO clock enabled. + * + * @param[in] config the STM32 ports configuration + * + * @notapi + */ +void _pal_lld_init(const PALConfig *config) { + + /* + * Enables the GPIO related clocks. + */ + rccEnableAPB2(APB2_EN_MASK, FALSE); + + /* + * Initial GPIO setup. + */ + GPIOA->ODR = config->PAData.odr; + GPIOA->CRH = config->PAData.crh; + GPIOA->CRL = config->PAData.crl; + GPIOB->ODR = config->PBData.odr; + GPIOB->CRH = config->PBData.crh; + GPIOB->CRL = config->PBData.crl; + GPIOC->ODR = config->PCData.odr; + GPIOC->CRH = config->PCData.crh; + GPIOC->CRL = config->PCData.crl; + GPIOD->ODR = config->PDData.odr; + GPIOD->CRH = config->PDData.crh; + GPIOD->CRL = config->PDData.crl; +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) + GPIOE->ODR = config->PEData.odr; + GPIOE->CRH = config->PEData.crh; + GPIOE->CRL = config->PEData.crl; +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) + GPIOF->ODR = config->PFData.odr; + GPIOF->CRH = config->PFData.crh; + GPIOF->CRL = config->PFData.crl; +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) + GPIOG->ODR = config->PGData.odr; + GPIOG->CRH = config->PGData.crh; + GPIOG->CRL = config->PGData.crl; +#endif +#endif +#endif +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note @p PAL_MODE_UNCONNECTED is implemented as push pull output at 2MHz. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) { + static const uint8_t cfgtab[] = { + 4, /* PAL_MODE_RESET, implemented as input.*/ + 2, /* PAL_MODE_UNCONNECTED, implemented as push pull output 2MHz.*/ + 4, /* PAL_MODE_INPUT */ + 8, /* PAL_MODE_INPUT_PULLUP */ + 8, /* PAL_MODE_INPUT_PULLDOWN */ + 0, /* PAL_MODE_INPUT_ANALOG */ + 3, /* PAL_MODE_OUTPUT_PUSHPULL, 50MHz.*/ + 7, /* PAL_MODE_OUTPUT_OPENDRAIN, 50MHz.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 8, /* Reserved.*/ + 0xB, /* PAL_MODE_STM32_ALTERNATE_PUSHPULL, 50MHz.*/ + 0xF, /* PAL_MODE_STM32_ALTERNATE_OPENDRAIN, 50MHz.*/ + }; + uint32_t mh, ml, crh, crl, cfg; + unsigned i; + + if (mode == PAL_MODE_INPUT_PULLUP) + port->BSRR = mask; + else if (mode == PAL_MODE_INPUT_PULLDOWN) + port->BRR = mask; + cfg = cfgtab[mode]; + mh = ml = crh = crl = 0; + for (i = 0; i < 8; i++) { + ml <<= 4; + mh <<= 4; + crl <<= 4; + crh <<= 4; + if ((mask & 0x0080) == 0) + ml |= 0xf; + else + crl |= cfg; + if ((mask & 0x8000) == 0) + mh |= 0xf; + else + crh |= cfg; + mask <<= 1; + } + port->CRH = (port->CRH & mh) | crh; + port->CRL = (port->CRL & ml) | crl; +} + +#endif /* HAL_USE_PAL */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.h b/os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.h new file mode 100644 index 000000000..348a55f2e --- /dev/null +++ b/os/hal/ports/STM32/LLD/GPIOv1/hal_pal_lld.h @@ -0,0 +1,376 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/GPIOv1/pal_lld.h + * @brief STM32 PAL low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef _PAL_LLD_H_ +#define _PAL_LLD_H_ + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +/** + * @name STM32-specific I/O mode flags + * @{ + */ +/** + * @brief STM32 specific alternate push-pull output mode. + */ +#define PAL_MODE_STM32_ALTERNATE_PUSHPULL 16 + +/** + * @brief STM32 specific alternate open-drain output mode. + */ +#define PAL_MODE_STM32_ALTERNATE_OPENDRAIN 17 +/** @} */ + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +/** + * @name Port related definitions + * @{ + */ +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 16 + +/** + * @brief Whole port mask. + * @details This macro specifies all the valid bits into a port. + */ +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF) +/** @} */ + +/** + * @name Line handling macros + * @{ + */ +/** + * @brief Forms a line identifier. + * @details A port/pad pair are encoded into an @p ioline_t type. The encoding + * of this type is platform-dependent. + * @note In this driver the pad number is encoded in the lower 4 bits of + * the GPIO address which are guaranteed to be zero. + */ +#define PAL_LINE(port, pad) \ + ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad))) + +/** + * @brief Decodes a port identifier from a line identifier. + */ +#define PAL_PORT(line) \ + ((stm32_gpio_t *)(((uint32_t)(line)) & 0xFFFFFFF0U)) + +/** + * @brief Decodes a pad identifier from a line identifier. + */ +#define PAL_PAD(line) \ + ((uint32_t)((uint32_t)(line) & 0x0000000FU)) + +/** + * @brief Value identifying an invalid line. + */ +#define PAL_NOLINE 0U +/** @} */ + +/** + * @brief GPIO port setup info. + */ +typedef struct { + /** Initial value for ODR register.*/ + uint32_t odr; + /** Initial value for CRL register.*/ + uint32_t crl; + /** Initial value for CRH register.*/ + uint32_t crh; +} stm32_gpio_setup_t; + +/** + * @brief STM32 GPIO static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialize the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + */ +typedef struct { + /** @brief Port A setup data.*/ + stm32_gpio_setup_t PAData; + /** @brief Port B setup data.*/ + stm32_gpio_setup_t PBData; + /** @brief Port C setup data.*/ + stm32_gpio_setup_t PCData; + /** @brief Port D setup data.*/ + stm32_gpio_setup_t PDData; +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) + /** @brief Port E setup data.*/ + stm32_gpio_setup_t PEData; +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) + /** @brief Port F setup data.*/ + stm32_gpio_setup_t PFData; +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) + /** @brief Port G setup data.*/ + stm32_gpio_setup_t PGData; +#endif +#endif +#endif +} PALConfig; + +/** + * @brief Digital I/O port sized unsigned type. + */ +typedef uint32_t ioportmask_t; + +/** + * @brief Digital I/O modes. + */ +typedef uint32_t iomode_t; + +/** + * @brief Type of an I/O line. + */ +typedef uint32_t ioline_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef GPIO_TypeDef * ioportid_t; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/* The low level driver wraps the definitions already present in the STM32 */ +/* firmware library. */ +/*===========================================================================*/ + +/** + * @brief GPIO port A identifier. + */ +#if STM32_HAS_GPIOA || defined(__DOXYGEN__) +#define IOPORT1 GPIOA +#endif + +/** + * @brief GPIO port B identifier. + */ +#if STM32_HAS_GPIOB || defined(__DOXYGEN__) +#define IOPORT2 GPIOB +#endif + +/** + * @brief GPIO port C identifier. + */ +#if STM32_HAS_GPIOC || defined(__DOXYGEN__) +#define IOPORT3 GPIOC +#endif + +/** + * @brief GPIO port D identifier. + */ +#if STM32_HAS_GPIOD || defined(__DOXYGEN__) +#define IOPORT4 GPIOD +#endif + +/** + * @brief GPIO port E identifier. + */ +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) +#define IOPORT5 GPIOE +#endif + +/** + * @brief GPIO port F identifier. + */ +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) +#define IOPORT6 GPIOF +#endif + +/** + * @brief GPIO port G identifier. + */ +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) +#define IOPORT7 GPIOG +#endif + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief GPIO ports subsystem initialization. + * + * @notapi + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads an I/O port. + * @details This function is implemented by reading the GPIO IDR register, the + * implementation has no side effects. + * @note This function is not meant to be invoked directly by the application + * code. + * + * @param[in] port port identifier + * @return The port bits. + * + * @notapi + */ +#define pal_lld_readport(port) ((port)->IDR) + +/** + * @brief Reads the output latch. + * @details This function is implemented by reading the GPIO ODR register, the + * implementation has no side effects. + * @note This function is not meant to be invoked directly by the application + * code. + * + * @param[in] port port identifier + * @return The latched logical states. + * + * @notapi + */ +#define pal_lld_readlatch(port) ((port)->ODR) + +/** + * @brief Writes on a I/O port. + * @details This function is implemented by writing the GPIO ODR register, the + * implementation has no side effects. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port port identifier + * @param[in] bits bits to be written on the specified port + * + * @notapi + */ +#define pal_lld_writeport(port, bits) ((port)->ODR = (bits)) + +/** + * @brief Sets a bits mask on a I/O port. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port port identifier + * @param[in] bits bits to be ORed on the specified port + * + * @notapi + */ +#define pal_lld_setport(port, bits) ((port)->BSRR = (bits)) + +/** + * @brief Clears a bits mask on a I/O port. + * @details This function is implemented by writing the GPIO BRR register, the + * implementation has no side effects. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port port identifier + * @param[in] bits bits to be cleared on the specified port + * + * @notapi + */ +#define pal_lld_clearport(port, bits) ((port)->BRR = (bits)) + +/** + * @brief Writes a group of bits. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset the group bit offset within the port + * @param[in] bits bits to be written. Values exceeding the group + * width are masked. + * + * @notapi + */ +#define pal_lld_writegroup(port, mask, offset, bits) \ + ((port)->BSRR = ((~(bits) & (mask)) << (16 + (offset))) | \ + (((bits) & (mask)) << (offset))) + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] mode group mode + * + * @notapi + */ +#define pal_lld_setgroupmode(port, mask, offset, mode) \ + _pal_lld_setgroupmode(port, mask << offset, mode) + +/** + * @brief Writes a logical state on an output pad. + * @note Writing on pads programmed as pull-up or pull-down has the side + * effect to modify the resistor setting because the output latched + * data is used for the resistor selection. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) + +extern const PALConfig pal_default_config; + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PAL */ + +#endif /* _PAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv1/pal_lld.c b/os/hal/ports/STM32/LLD/GPIOv1/pal_lld.c deleted file mode 100644 index f1edb10b5..000000000 --- a/os/hal/ports/STM32/LLD/GPIOv1/pal_lld.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/GPIOv1/pal_lld.c - * @brief STM32 PAL low level driver code. - * - * @addtogroup PAL - * @{ - */ - -#include "hal.h" - -#if HAL_USE_PAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if STM32_HAS_GPIOG -#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \ - RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \ - RCC_APB2ENR_IOPEEN | RCC_APB2ENR_IOPFEN | \ - RCC_APB2ENR_IOPGEN | RCC_APB2ENR_AFIOEN) -#elif STM32_HAS_GPIOE -#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \ - RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \ - RCC_APB2ENR_IOPEEN | RCC_APB2ENR_AFIOEN) -#else -#define APB2_EN_MASK (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | \ - RCC_APB2ENR_IOPCEN | RCC_APB2ENR_IOPDEN | \ - RCC_APB2ENR_AFIOEN) -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief STM32 I/O ports configuration. - * @details Ports A-D(E, F, G) clocks enabled, AFIO clock enabled. - * - * @param[in] config the STM32 ports configuration - * - * @notapi - */ -void _pal_lld_init(const PALConfig *config) { - - /* - * Enables the GPIO related clocks. - */ - rccEnableAPB2(APB2_EN_MASK, FALSE); - - /* - * Initial GPIO setup. - */ - GPIOA->ODR = config->PAData.odr; - GPIOA->CRH = config->PAData.crh; - GPIOA->CRL = config->PAData.crl; - GPIOB->ODR = config->PBData.odr; - GPIOB->CRH = config->PBData.crh; - GPIOB->CRL = config->PBData.crl; - GPIOC->ODR = config->PCData.odr; - GPIOC->CRH = config->PCData.crh; - GPIOC->CRL = config->PCData.crl; - GPIOD->ODR = config->PDData.odr; - GPIOD->CRH = config->PDData.crh; - GPIOD->CRL = config->PDData.crl; -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) - GPIOE->ODR = config->PEData.odr; - GPIOE->CRH = config->PEData.crh; - GPIOE->CRL = config->PEData.crl; -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) - GPIOF->ODR = config->PFData.odr; - GPIOF->CRH = config->PFData.crh; - GPIOF->CRL = config->PFData.crl; -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) - GPIOG->ODR = config->PGData.odr; - GPIOG->CRH = config->PGData.crh; - GPIOG->CRL = config->PGData.crl; -#endif -#endif -#endif -} - -/** - * @brief Pads mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * @note @p PAL_MODE_UNCONNECTED is implemented as push pull output at 2MHz. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port the port identifier - * @param[in] mask the group mask - * @param[in] mode the mode - * - * @notapi - */ -void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode) { - static const uint8_t cfgtab[] = { - 4, /* PAL_MODE_RESET, implemented as input.*/ - 2, /* PAL_MODE_UNCONNECTED, implemented as push pull output 2MHz.*/ - 4, /* PAL_MODE_INPUT */ - 8, /* PAL_MODE_INPUT_PULLUP */ - 8, /* PAL_MODE_INPUT_PULLDOWN */ - 0, /* PAL_MODE_INPUT_ANALOG */ - 3, /* PAL_MODE_OUTPUT_PUSHPULL, 50MHz.*/ - 7, /* PAL_MODE_OUTPUT_OPENDRAIN, 50MHz.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 8, /* Reserved.*/ - 0xB, /* PAL_MODE_STM32_ALTERNATE_PUSHPULL, 50MHz.*/ - 0xF, /* PAL_MODE_STM32_ALTERNATE_OPENDRAIN, 50MHz.*/ - }; - uint32_t mh, ml, crh, crl, cfg; - unsigned i; - - if (mode == PAL_MODE_INPUT_PULLUP) - port->BSRR = mask; - else if (mode == PAL_MODE_INPUT_PULLDOWN) - port->BRR = mask; - cfg = cfgtab[mode]; - mh = ml = crh = crl = 0; - for (i = 0; i < 8; i++) { - ml <<= 4; - mh <<= 4; - crl <<= 4; - crh <<= 4; - if ((mask & 0x0080) == 0) - ml |= 0xf; - else - crl |= cfg; - if ((mask & 0x8000) == 0) - mh |= 0xf; - else - crh |= cfg; - mask <<= 1; - } - port->CRH = (port->CRH & mh) | crh; - port->CRL = (port->CRL & ml) | crl; -} - -#endif /* HAL_USE_PAL */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv1/pal_lld.h b/os/hal/ports/STM32/LLD/GPIOv1/pal_lld.h deleted file mode 100644 index 348a55f2e..000000000 --- a/os/hal/ports/STM32/LLD/GPIOv1/pal_lld.h +++ /dev/null @@ -1,376 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/GPIOv1/pal_lld.h - * @brief STM32 PAL low level driver header. - * - * @addtogroup PAL - * @{ - */ - -#ifndef _PAL_LLD_H_ -#define _PAL_LLD_H_ - -#if HAL_USE_PAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Unsupported modes and specific modes */ -/*===========================================================================*/ - -/** - * @name STM32-specific I/O mode flags - * @{ - */ -/** - * @brief STM32 specific alternate push-pull output mode. - */ -#define PAL_MODE_STM32_ALTERNATE_PUSHPULL 16 - -/** - * @brief STM32 specific alternate open-drain output mode. - */ -#define PAL_MODE_STM32_ALTERNATE_OPENDRAIN 17 -/** @} */ - -/*===========================================================================*/ -/* I/O Ports Types and constants. */ -/*===========================================================================*/ - -/** - * @name Port related definitions - * @{ - */ -/** - * @brief Width, in bits, of an I/O port. - */ -#define PAL_IOPORTS_WIDTH 16 - -/** - * @brief Whole port mask. - * @details This macro specifies all the valid bits into a port. - */ -#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF) -/** @} */ - -/** - * @name Line handling macros - * @{ - */ -/** - * @brief Forms a line identifier. - * @details A port/pad pair are encoded into an @p ioline_t type. The encoding - * of this type is platform-dependent. - * @note In this driver the pad number is encoded in the lower 4 bits of - * the GPIO address which are guaranteed to be zero. - */ -#define PAL_LINE(port, pad) \ - ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad))) - -/** - * @brief Decodes a port identifier from a line identifier. - */ -#define PAL_PORT(line) \ - ((stm32_gpio_t *)(((uint32_t)(line)) & 0xFFFFFFF0U)) - -/** - * @brief Decodes a pad identifier from a line identifier. - */ -#define PAL_PAD(line) \ - ((uint32_t)((uint32_t)(line) & 0x0000000FU)) - -/** - * @brief Value identifying an invalid line. - */ -#define PAL_NOLINE 0U -/** @} */ - -/** - * @brief GPIO port setup info. - */ -typedef struct { - /** Initial value for ODR register.*/ - uint32_t odr; - /** Initial value for CRL register.*/ - uint32_t crl; - /** Initial value for CRH register.*/ - uint32_t crh; -} stm32_gpio_setup_t; - -/** - * @brief STM32 GPIO static initializer. - * @details An instance of this structure must be passed to @p palInit() at - * system startup time in order to initialize the digital I/O - * subsystem. This represents only the initial setup, specific pads - * or whole ports can be reprogrammed at later time. - */ -typedef struct { - /** @brief Port A setup data.*/ - stm32_gpio_setup_t PAData; - /** @brief Port B setup data.*/ - stm32_gpio_setup_t PBData; - /** @brief Port C setup data.*/ - stm32_gpio_setup_t PCData; - /** @brief Port D setup data.*/ - stm32_gpio_setup_t PDData; -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) - /** @brief Port E setup data.*/ - stm32_gpio_setup_t PEData; -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) - /** @brief Port F setup data.*/ - stm32_gpio_setup_t PFData; -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) - /** @brief Port G setup data.*/ - stm32_gpio_setup_t PGData; -#endif -#endif -#endif -} PALConfig; - -/** - * @brief Digital I/O port sized unsigned type. - */ -typedef uint32_t ioportmask_t; - -/** - * @brief Digital I/O modes. - */ -typedef uint32_t iomode_t; - -/** - * @brief Type of an I/O line. - */ -typedef uint32_t ioline_t; - -/** - * @brief Port Identifier. - * @details This type can be a scalar or some kind of pointer, do not make - * any assumption about it, use the provided macros when populating - * variables of this type. - */ -typedef GPIO_TypeDef * ioportid_t; - -/*===========================================================================*/ -/* I/O Ports Identifiers. */ -/* The low level driver wraps the definitions already present in the STM32 */ -/* firmware library. */ -/*===========================================================================*/ - -/** - * @brief GPIO port A identifier. - */ -#if STM32_HAS_GPIOA || defined(__DOXYGEN__) -#define IOPORT1 GPIOA -#endif - -/** - * @brief GPIO port B identifier. - */ -#if STM32_HAS_GPIOB || defined(__DOXYGEN__) -#define IOPORT2 GPIOB -#endif - -/** - * @brief GPIO port C identifier. - */ -#if STM32_HAS_GPIOC || defined(__DOXYGEN__) -#define IOPORT3 GPIOC -#endif - -/** - * @brief GPIO port D identifier. - */ -#if STM32_HAS_GPIOD || defined(__DOXYGEN__) -#define IOPORT4 GPIOD -#endif - -/** - * @brief GPIO port E identifier. - */ -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) -#define IOPORT5 GPIOE -#endif - -/** - * @brief GPIO port F identifier. - */ -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) -#define IOPORT6 GPIOF -#endif - -/** - * @brief GPIO port G identifier. - */ -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) -#define IOPORT7 GPIOG -#endif - -/*===========================================================================*/ -/* Implementation, some of the following macros could be implemented as */ -/* functions, if so please put them in pal_lld.c. */ -/*===========================================================================*/ - -/** - * @brief GPIO ports subsystem initialization. - * - * @notapi - */ -#define pal_lld_init(config) _pal_lld_init(config) - -/** - * @brief Reads an I/O port. - * @details This function is implemented by reading the GPIO IDR register, the - * implementation has no side effects. - * @note This function is not meant to be invoked directly by the application - * code. - * - * @param[in] port port identifier - * @return The port bits. - * - * @notapi - */ -#define pal_lld_readport(port) ((port)->IDR) - -/** - * @brief Reads the output latch. - * @details This function is implemented by reading the GPIO ODR register, the - * implementation has no side effects. - * @note This function is not meant to be invoked directly by the application - * code. - * - * @param[in] port port identifier - * @return The latched logical states. - * - * @notapi - */ -#define pal_lld_readlatch(port) ((port)->ODR) - -/** - * @brief Writes on a I/O port. - * @details This function is implemented by writing the GPIO ODR register, the - * implementation has no side effects. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port port identifier - * @param[in] bits bits to be written on the specified port - * - * @notapi - */ -#define pal_lld_writeport(port, bits) ((port)->ODR = (bits)) - -/** - * @brief Sets a bits mask on a I/O port. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port port identifier - * @param[in] bits bits to be ORed on the specified port - * - * @notapi - */ -#define pal_lld_setport(port, bits) ((port)->BSRR = (bits)) - -/** - * @brief Clears a bits mask on a I/O port. - * @details This function is implemented by writing the GPIO BRR register, the - * implementation has no side effects. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port port identifier - * @param[in] bits bits to be cleared on the specified port - * - * @notapi - */ -#define pal_lld_clearport(port, bits) ((port)->BRR = (bits)) - -/** - * @brief Writes a group of bits. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset the group bit offset within the port - * @param[in] bits bits to be written. Values exceeding the group - * width are masked. - * - * @notapi - */ -#define pal_lld_writegroup(port, mask, offset, bits) \ - ((port)->BSRR = ((~(bits) & (mask)) << (16 + (offset))) | \ - (((bits) & (mask)) << (offset))) - -/** - * @brief Pads group mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset group bit offset within the port - * @param[in] mode group mode - * - * @notapi - */ -#define pal_lld_setgroupmode(port, mask, offset, mode) \ - _pal_lld_setgroupmode(port, mask << offset, mode) - -/** - * @brief Writes a logical state on an output pad. - * @note Writing on pads programmed as pull-up or pull-down has the side - * effect to modify the resistor setting because the output latched - * data is used for the resistor selection. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @param[in] bit logical value, the value must be @p PAL_LOW or - * @p PAL_HIGH - * - * @notapi - */ -#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) - -extern const PALConfig pal_default_config; - -#ifdef __cplusplus -extern "C" { -#endif - void _pal_lld_init(const PALConfig *config); - void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_PAL */ - -#endif /* _PAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.c b/os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.c new file mode 100644 index 000000000..3bf027d89 --- /dev/null +++ b/os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.c @@ -0,0 +1,212 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/GPIOv2/pal_lld.c + * @brief STM32 PAL low level driver code. + * + * @addtogroup PAL + * @{ + */ + +#include "hal.h" + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if defined(STM32L0XX) || defined(STM32L1XX) +#define AHB_EN_MASK STM32_GPIO_EN_MASK +#define AHB_LPEN_MASK AHB_EN_MASK + +#elif defined(STM32F0XX) || defined(STM32F3XX) || defined(STM32F37X) +#define AHB_EN_MASK STM32_GPIO_EN_MASK +#define AHB_LPEN_MASK 0 + +#elif defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) +#define AHB1_EN_MASK STM32_GPIO_EN_MASK +#define AHB1_LPEN_MASK AHB1_EN_MASK + +#else +#error "missing or unsupported platform for GPIOv2 PAL driver" +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void initgpio(stm32_gpio_t *gpiop, const stm32_gpio_setup_t *config) { + + gpiop->OTYPER = config->otyper; + gpiop->OSPEEDR = config->ospeedr; + gpiop->PUPDR = config->pupdr; + gpiop->ODR = config->odr; + gpiop->AFRL = config->afrl; + gpiop->AFRH = config->afrh; + gpiop->MODER = config->moder; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief STM32 I/O ports configuration. + * @details Ports A-D(E, F, G, H) clocks enabled. + * + * @param[in] config the STM32 ports configuration + * + * @notapi + */ +void _pal_lld_init(const PALConfig *config) { + + /* + * Enables the GPIO related clocks. + */ +#if defined(STM32L0XX) + RCC->IOPENR |= AHB_EN_MASK; + RCC->IOPSMENR |= AHB_LPEN_MASK; +#elif defined(STM32L1XX) + rccEnableAHB(AHB_EN_MASK, TRUE); + RCC->AHBLPENR |= AHB_LPEN_MASK; +#elif defined(STM32F0XX) + rccEnableAHB(AHB_EN_MASK, TRUE); +#elif defined(STM32F3XX) || defined(STM32F37X) + rccEnableAHB(AHB_EN_MASK, TRUE); +#elif defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) + RCC->AHB1ENR |= AHB1_EN_MASK; + RCC->AHB1LPENR |= AHB1_LPEN_MASK; +#endif + + /* + * Initial GPIO setup. + */ +#if STM32_HAS_GPIOA + initgpio(GPIOA, &config->PAData); +#endif +#if STM32_HAS_GPIOB + initgpio(GPIOB, &config->PBData); +#endif +#if STM32_HAS_GPIOC + initgpio(GPIOC, &config->PCData); +#endif +#if STM32_HAS_GPIOD + initgpio(GPIOD, &config->PDData); +#endif +#if STM32_HAS_GPIOE + initgpio(GPIOE, &config->PEData); +#endif +#if STM32_HAS_GPIOF + initgpio(GPIOF, &config->PFData); +#endif +#if STM32_HAS_GPIOG + initgpio(GPIOG, &config->PGData); +#endif +#if STM32_HAS_GPIOH + initgpio(GPIOH, &config->PHData); +#endif +#if STM32_HAS_GPIOI + initgpio(GPIOI, &config->PIData); +#endif +#if STM32_HAS_GPIOJ + initgpio(GPIOJ, &config->PJData); +#endif +#if STM32_HAS_GPIOK + initgpio(GPIOK, &config->PKData); +#endif +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note @p PAL_MODE_UNCONNECTED is implemented as push pull at minimum + * speed. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) { + + uint32_t moder = (mode & PAL_STM32_MODE_MASK) >> 0; + uint32_t otyper = (mode & PAL_STM32_OTYPE_MASK) >> 2; + uint32_t ospeedr = (mode & PAL_STM32_OSPEED_MASK) >> 3; + uint32_t pupdr = (mode & PAL_STM32_PUPDR_MASK) >> 5; + uint32_t altr = (mode & PAL_STM32_ALTERNATE_MASK) >> 7; + uint32_t bit = 0; + while (true) { + if ((mask & 1) != 0) { + uint32_t altrmask, m1, m2, m4; + + altrmask = altr << ((bit & 7) * 4); + m1 = 1 << bit; + m2 = 3 << (bit * 2); + m4 = 15 << ((bit & 7) * 4); + port->OTYPER = (port->OTYPER & ~m1) | otyper; + port->OSPEEDR = (port->OSPEEDR & ~m2) | ospeedr; + port->PUPDR = (port->PUPDR & ~m2) | pupdr; + if (moder == PAL_STM32_MODE_ALTERNATE) { + /* If going in alternate mode then the alternate number is set + before switching mode in order to avoid glitches.*/ + if (bit < 8) + port->AFRL = (port->AFRL & ~m4) | altrmask; + else + port->AFRH = (port->AFRH & ~m4) | altrmask; + port->MODER = (port->MODER & ~m2) | moder; + } + else { + /* If going into a non-alternate mode then the mode is switched + before setting the alternate mode in order to avoid glitches.*/ + port->MODER = (port->MODER & ~m2) | moder; + if (bit < 8) + port->AFRL = (port->AFRL & ~m4) | altrmask; + else + port->AFRH = (port->AFRH & ~m4) | altrmask; + } + } + mask >>= 1; + if (!mask) + return; + otyper <<= 1; + ospeedr <<= 2; + pupdr <<= 2; + moder <<= 2; + bit++; + } +} + +#endif /* HAL_USE_PAL */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.h b/os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.h new file mode 100644 index 000000000..4f11df50c --- /dev/null +++ b/os/hal/ports/STM32/LLD/GPIOv2/hal_pal_lld.h @@ -0,0 +1,559 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/GPIOv2/pal_lld.h + * @brief STM32 PAL low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef _PAL_LLD_H_ +#define _PAL_LLD_H_ + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +#undef PAL_MODE_RESET +#undef PAL_MODE_UNCONNECTED +#undef PAL_MODE_INPUT +#undef PAL_MODE_INPUT_PULLUP +#undef PAL_MODE_INPUT_PULLDOWN +#undef PAL_MODE_INPUT_ANALOG +#undef PAL_MODE_OUTPUT_PUSHPULL +#undef PAL_MODE_OUTPUT_OPENDRAIN + +/** + * @name STM32-specific I/O mode flags + * @{ + */ +#define PAL_STM32_MODE_MASK (3U << 0U) +#define PAL_STM32_MODE_INPUT (0U << 0U) +#define PAL_STM32_MODE_OUTPUT (1U << 0U) +#define PAL_STM32_MODE_ALTERNATE (2U << 0U) +#define PAL_STM32_MODE_ANALOG (3U << 0U) + +#define PAL_STM32_OTYPE_MASK (1U << 2U) +#define PAL_STM32_OTYPE_PUSHPULL (0U << 2U) +#define PAL_STM32_OTYPE_OPENDRAIN (1U << 2U) + +#define PAL_STM32_OSPEED_MASK (3U << 3U) +#define PAL_STM32_OSPEED_LOWEST (0U << 3U) +#if defined(STM32F0XX) || defined(STM32F30X) || defined(STM32F37X) +#define PAL_STM32_OSPEED_MID (1U << 3U) +#else +#define PAL_STM32_OSPEED_MID1 (1U << 3U) +#define PAL_STM32_OSPEED_MID2 (2U << 3U) +#endif +#define PAL_STM32_OSPEED_HIGHEST (3U << 3U) + +#define PAL_STM32_PUPDR_MASK (3U << 5U) +#define PAL_STM32_PUPDR_FLOATING (0U << 5U) +#define PAL_STM32_PUPDR_PULLUP (1U << 5U) +#define PAL_STM32_PUPDR_PULLDOWN (2U << 5U) + +#define PAL_STM32_ALTERNATE_MASK (15U << 7U) +#define PAL_STM32_ALTERNATE(n) ((n) << 7U) + +/** + * @brief Alternate function. + * + * @param[in] n alternate function selector + */ +#define PAL_MODE_ALTERNATE(n) (PAL_STM32_MODE_ALTERNATE | \ + PAL_STM32_ALTERNATE(n)) +/** @} */ + +/** + * @name Standard I/O mode flags + * @{ + */ +/** + * @brief This mode is implemented as input. + */ +#define PAL_MODE_RESET PAL_STM32_MODE_INPUT + +/** + * @brief This mode is implemented as input with pull-up. + */ +#define PAL_MODE_UNCONNECTED PAL_MODE_INPUT_PULLUP + +/** + * @brief Regular input high-Z pad. + */ +#define PAL_MODE_INPUT PAL_STM32_MODE_INPUT + +/** + * @brief Input pad with weak pull up resistor. + */ +#define PAL_MODE_INPUT_PULLUP (PAL_STM32_MODE_INPUT | \ + PAL_STM32_PUPDR_PULLUP) + +/** + * @brief Input pad with weak pull down resistor. + */ +#define PAL_MODE_INPUT_PULLDOWN (PAL_STM32_MODE_INPUT | \ + PAL_STM32_PUPDR_PULLDOWN) + +/** + * @brief Analog input mode. + */ +#define PAL_MODE_INPUT_ANALOG PAL_STM32_MODE_ANALOG + +/** + * @brief Push-pull output pad. + */ +#define PAL_MODE_OUTPUT_PUSHPULL (PAL_STM32_MODE_OUTPUT | \ + PAL_STM32_OTYPE_PUSHPULL) + +/** + * @brief Open-drain output pad. + */ +#define PAL_MODE_OUTPUT_OPENDRAIN (PAL_STM32_MODE_OUTPUT | \ + PAL_STM32_OTYPE_OPENDRAIN) +/** @} */ + +/* Discarded definitions from the ST headers, the PAL driver uses its own + definitions in order to have an unified handling for all devices. + Unfortunately the ST headers have no uniform definitions for the same + objects across the various sub-families.*/ +#undef GPIOA +#undef GPIOB +#undef GPIOC +#undef GPIOD +#undef GPIOE +#undef GPIOF +#undef GPIOG +#undef GPIOH +#undef GPIOI +#undef GPIOJ +#undef GPIOK + +/** + * @name GPIO ports definitions + * @{ + */ +#define GPIOA ((stm32_gpio_t *)GPIOA_BASE) +#define GPIOB ((stm32_gpio_t *)GPIOB_BASE) +#define GPIOC ((stm32_gpio_t *)GPIOC_BASE) +#define GPIOD ((stm32_gpio_t *)GPIOD_BASE) +#define GPIOE ((stm32_gpio_t *)GPIOE_BASE) +#define GPIOF ((stm32_gpio_t *)GPIOF_BASE) +#define GPIOG ((stm32_gpio_t *)GPIOG_BASE) +#define GPIOH ((stm32_gpio_t *)GPIOH_BASE) +#define GPIOI ((stm32_gpio_t *)GPIOI_BASE) +#define GPIOJ ((stm32_gpio_t *)GPIOJ_BASE) +#define GPIOK ((stm32_gpio_t *)GPIOK_BASE) +/** @} */ + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +/** + * @name Port related definitions + * @{ + */ +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 16 + +/** + * @brief Whole port mask. + * @details This macro specifies all the valid bits into a port. + */ +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF) +/** @} */ + +/** + * @name Line handling macros + * @{ + */ +/** + * @brief Forms a line identifier. + * @details A port/pad pair are encoded into an @p ioline_t type. The encoding + * of this type is platform-dependent. + * @note In this driver the pad number is encoded in the lower 4 bits of + * the GPIO address which are guaranteed to be zero. + */ +#define PAL_LINE(port, pad) \ + ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad))) + +/** + * @brief Decodes a port identifier from a line identifier. + */ +#define PAL_PORT(line) \ + ((stm32_gpio_t *)(((uint32_t)(line)) & 0xFFFFFFF0U)) + +/** + * @brief Decodes a pad identifier from a line identifier. + */ +#define PAL_PAD(line) \ + ((uint32_t)((uint32_t)(line) & 0x0000000FU)) + +/** + * @brief Value identifying an invalid line. + */ +#define PAL_NOLINE 0U +/** @} */ + +/** + * @brief STM32 GPIO registers block. + */ +typedef struct { + + volatile uint32_t MODER; + volatile uint32_t OTYPER; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile union { + uint32_t W; + struct { + uint16_t set; + uint16_t clear; + } H; + } BSRR; + volatile uint32_t LCKR; + volatile uint32_t AFRL; + volatile uint32_t AFRH; + volatile uint32_t BRR; +} stm32_gpio_t; + +/** + * @brief GPIO port setup info. + */ +typedef struct { + /** Initial value for MODER register.*/ + uint32_t moder; + /** Initial value for OTYPER register.*/ + uint32_t otyper; + /** Initial value for OSPEEDR register.*/ + uint32_t ospeedr; + /** Initial value for PUPDR register.*/ + uint32_t pupdr; + /** Initial value for ODR register.*/ + uint32_t odr; + /** Initial value for AFRL register.*/ + uint32_t afrl; + /** Initial value for AFRH register.*/ + uint32_t afrh; +} stm32_gpio_setup_t; + +/** + * @brief STM32 GPIO static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialize the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + */ +typedef struct { +#if STM32_HAS_GPIOA || defined(__DOXYGEN__) + /** @brief Port A setup data.*/ + stm32_gpio_setup_t PAData; +#endif +#if STM32_HAS_GPIOB || defined(__DOXYGEN__) + /** @brief Port B setup data.*/ + stm32_gpio_setup_t PBData; +#endif +#if STM32_HAS_GPIOC || defined(__DOXYGEN__) + /** @brief Port C setup data.*/ + stm32_gpio_setup_t PCData; +#endif +#if STM32_HAS_GPIOD || defined(__DOXYGEN__) + /** @brief Port D setup data.*/ + stm32_gpio_setup_t PDData; +#endif +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) + /** @brief Port E setup data.*/ + stm32_gpio_setup_t PEData; +#endif +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) + /** @brief Port F setup data.*/ + stm32_gpio_setup_t PFData; +#endif +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) + /** @brief Port G setup data.*/ + stm32_gpio_setup_t PGData; +#endif +#if STM32_HAS_GPIOH || defined(__DOXYGEN__) + /** @brief Port H setup data.*/ + stm32_gpio_setup_t PHData; +#endif +#if STM32_HAS_GPIOI || defined(__DOXYGEN__) + /** @brief Port I setup data.*/ + stm32_gpio_setup_t PIData; +#endif +#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) + /** @brief Port I setup data.*/ + stm32_gpio_setup_t PJData; +#endif +#if STM32_HAS_GPIOK || defined(__DOXYGEN__) + /** @brief Port I setup data.*/ + stm32_gpio_setup_t PKData; +#endif +} PALConfig; + +/** + * @brief Type of digital I/O port sized unsigned integer. + */ +typedef uint32_t ioportmask_t; + +/** + * @brief Type of digital I/O modes. + */ +typedef uint32_t iomode_t; + +/** + * @brief Type of an I/O line. + */ +typedef uint32_t ioline_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef stm32_gpio_t * ioportid_t; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/* The low level driver wraps the definitions already present in the STM32 */ +/* firmware library. */ +/*===========================================================================*/ + +/** + * @brief GPIO port A identifier. + */ +#if STM32_HAS_GPIOA || defined(__DOXYGEN__) +#define IOPORT1 GPIOA +#endif + +/** + * @brief GPIO port B identifier. + */ +#if STM32_HAS_GPIOB || defined(__DOXYGEN__) +#define IOPORT2 GPIOB +#endif + +/** + * @brief GPIO port C identifier. + */ +#if STM32_HAS_GPIOC || defined(__DOXYGEN__) +#define IOPORT3 GPIOC +#endif + +/** + * @brief GPIO port D identifier. + */ +#if STM32_HAS_GPIOD || defined(__DOXYGEN__) +#define IOPORT4 GPIOD +#endif + +/** + * @brief GPIO port E identifier. + */ +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) +#define IOPORT5 GPIOE +#endif + +/** + * @brief GPIO port F identifier. + */ +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) +#define IOPORT6 GPIOF +#endif + +/** + * @brief GPIO port G identifier. + */ +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) +#define IOPORT7 GPIOG +#endif + +/** + * @brief GPIO port H identifier. + */ +#if STM32_HAS_GPIOH || defined(__DOXYGEN__) +#define IOPORT8 GPIOH +#endif + +/** + * @brief GPIO port I identifier. + */ +#if STM32_HAS_GPIOI || defined(__DOXYGEN__) +#define IOPORT9 GPIOI +#endif + +/** + * @brief GPIO port J identifier. + */ +#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) +#define IOPORT10 GPIOJ +#endif + +/** + * @brief GPIO port K identifier. + */ +#if STM32_HAS_GPIOK || defined(__DOXYGEN__) +#define IOPORT11 GPIOK +#endif + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief GPIO ports subsystem initialization. + * + * @notapi + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads an I/O port. + * @details This function is implemented by reading the GPIO IDR register, the + * implementation has no side effects. + * @note This function is not meant to be invoked directly by the application + * code. + * + * @param[in] port port identifier + * @return The port bits. + * + * @notapi + */ +#define pal_lld_readport(port) ((port)->IDR) + +/** + * @brief Reads the output latch. + * @details This function is implemented by reading the GPIO ODR register, the + * implementation has no side effects. + * @note This function is not meant to be invoked directly by the application + * code. + * + * @param[in] port port identifier + * @return The latched logical states. + * + * @notapi + */ +#define pal_lld_readlatch(port) ((port)->ODR) + +/** + * @brief Writes on a I/O port. + * @details This function is implemented by writing the GPIO ODR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] bits bits to be written on the specified port + * + * @notapi + */ +#define pal_lld_writeport(port, bits) ((port)->ODR = (bits)) + +/** + * @brief Sets a bits mask on a I/O port. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] bits bits to be ORed on the specified port + * + * @notapi + */ +#define pal_lld_setport(port, bits) ((port)->BSRR.H.set = (uint16_t)(bits)) + +/** + * @brief Clears a bits mask on a I/O port. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] bits bits to be cleared on the specified port + * + * @notapi + */ +#define pal_lld_clearport(port, bits) ((port)->BSRR.H.clear = (uint16_t)(bits)) + +/** + * @brief Writes a group of bits. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset the group bit offset within the port + * @param[in] bits bits to be written. Values exceeding the group + * width are masked. + * + * @notapi + */ +#define pal_lld_writegroup(port, mask, offset, bits) \ + ((port)->BSRR.W = ((~(bits) & (mask)) << (16U + (offset))) | \ + (((bits) & (mask)) << (offset))) + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] mode group mode + * + * @notapi + */ +#define pal_lld_setgroupmode(port, mask, offset, mode) \ + _pal_lld_setgroupmode(port, mask << offset, mode) + +/** + * @brief Writes a logical state on an output pad. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) + +extern const PALConfig pal_default_config; + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PAL */ + +#endif /* _PAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv2/pal_lld.c b/os/hal/ports/STM32/LLD/GPIOv2/pal_lld.c deleted file mode 100644 index 3bf027d89..000000000 --- a/os/hal/ports/STM32/LLD/GPIOv2/pal_lld.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/GPIOv2/pal_lld.c - * @brief STM32 PAL low level driver code. - * - * @addtogroup PAL - * @{ - */ - -#include "hal.h" - -#if HAL_USE_PAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if defined(STM32L0XX) || defined(STM32L1XX) -#define AHB_EN_MASK STM32_GPIO_EN_MASK -#define AHB_LPEN_MASK AHB_EN_MASK - -#elif defined(STM32F0XX) || defined(STM32F3XX) || defined(STM32F37X) -#define AHB_EN_MASK STM32_GPIO_EN_MASK -#define AHB_LPEN_MASK 0 - -#elif defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) -#define AHB1_EN_MASK STM32_GPIO_EN_MASK -#define AHB1_LPEN_MASK AHB1_EN_MASK - -#else -#error "missing or unsupported platform for GPIOv2 PAL driver" -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void initgpio(stm32_gpio_t *gpiop, const stm32_gpio_setup_t *config) { - - gpiop->OTYPER = config->otyper; - gpiop->OSPEEDR = config->ospeedr; - gpiop->PUPDR = config->pupdr; - gpiop->ODR = config->odr; - gpiop->AFRL = config->afrl; - gpiop->AFRH = config->afrh; - gpiop->MODER = config->moder; -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief STM32 I/O ports configuration. - * @details Ports A-D(E, F, G, H) clocks enabled. - * - * @param[in] config the STM32 ports configuration - * - * @notapi - */ -void _pal_lld_init(const PALConfig *config) { - - /* - * Enables the GPIO related clocks. - */ -#if defined(STM32L0XX) - RCC->IOPENR |= AHB_EN_MASK; - RCC->IOPSMENR |= AHB_LPEN_MASK; -#elif defined(STM32L1XX) - rccEnableAHB(AHB_EN_MASK, TRUE); - RCC->AHBLPENR |= AHB_LPEN_MASK; -#elif defined(STM32F0XX) - rccEnableAHB(AHB_EN_MASK, TRUE); -#elif defined(STM32F3XX) || defined(STM32F37X) - rccEnableAHB(AHB_EN_MASK, TRUE); -#elif defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) - RCC->AHB1ENR |= AHB1_EN_MASK; - RCC->AHB1LPENR |= AHB1_LPEN_MASK; -#endif - - /* - * Initial GPIO setup. - */ -#if STM32_HAS_GPIOA - initgpio(GPIOA, &config->PAData); -#endif -#if STM32_HAS_GPIOB - initgpio(GPIOB, &config->PBData); -#endif -#if STM32_HAS_GPIOC - initgpio(GPIOC, &config->PCData); -#endif -#if STM32_HAS_GPIOD - initgpio(GPIOD, &config->PDData); -#endif -#if STM32_HAS_GPIOE - initgpio(GPIOE, &config->PEData); -#endif -#if STM32_HAS_GPIOF - initgpio(GPIOF, &config->PFData); -#endif -#if STM32_HAS_GPIOG - initgpio(GPIOG, &config->PGData); -#endif -#if STM32_HAS_GPIOH - initgpio(GPIOH, &config->PHData); -#endif -#if STM32_HAS_GPIOI - initgpio(GPIOI, &config->PIData); -#endif -#if STM32_HAS_GPIOJ - initgpio(GPIOJ, &config->PJData); -#endif -#if STM32_HAS_GPIOK - initgpio(GPIOK, &config->PKData); -#endif -} - -/** - * @brief Pads mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * @note @p PAL_MODE_UNCONNECTED is implemented as push pull at minimum - * speed. - * - * @param[in] port the port identifier - * @param[in] mask the group mask - * @param[in] mode the mode - * - * @notapi - */ -void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode) { - - uint32_t moder = (mode & PAL_STM32_MODE_MASK) >> 0; - uint32_t otyper = (mode & PAL_STM32_OTYPE_MASK) >> 2; - uint32_t ospeedr = (mode & PAL_STM32_OSPEED_MASK) >> 3; - uint32_t pupdr = (mode & PAL_STM32_PUPDR_MASK) >> 5; - uint32_t altr = (mode & PAL_STM32_ALTERNATE_MASK) >> 7; - uint32_t bit = 0; - while (true) { - if ((mask & 1) != 0) { - uint32_t altrmask, m1, m2, m4; - - altrmask = altr << ((bit & 7) * 4); - m1 = 1 << bit; - m2 = 3 << (bit * 2); - m4 = 15 << ((bit & 7) * 4); - port->OTYPER = (port->OTYPER & ~m1) | otyper; - port->OSPEEDR = (port->OSPEEDR & ~m2) | ospeedr; - port->PUPDR = (port->PUPDR & ~m2) | pupdr; - if (moder == PAL_STM32_MODE_ALTERNATE) { - /* If going in alternate mode then the alternate number is set - before switching mode in order to avoid glitches.*/ - if (bit < 8) - port->AFRL = (port->AFRL & ~m4) | altrmask; - else - port->AFRH = (port->AFRH & ~m4) | altrmask; - port->MODER = (port->MODER & ~m2) | moder; - } - else { - /* If going into a non-alternate mode then the mode is switched - before setting the alternate mode in order to avoid glitches.*/ - port->MODER = (port->MODER & ~m2) | moder; - if (bit < 8) - port->AFRL = (port->AFRL & ~m4) | altrmask; - else - port->AFRH = (port->AFRH & ~m4) | altrmask; - } - } - mask >>= 1; - if (!mask) - return; - otyper <<= 1; - ospeedr <<= 2; - pupdr <<= 2; - moder <<= 2; - bit++; - } -} - -#endif /* HAL_USE_PAL */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv2/pal_lld.h b/os/hal/ports/STM32/LLD/GPIOv2/pal_lld.h deleted file mode 100644 index 4f11df50c..000000000 --- a/os/hal/ports/STM32/LLD/GPIOv2/pal_lld.h +++ /dev/null @@ -1,559 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/GPIOv2/pal_lld.h - * @brief STM32 PAL low level driver header. - * - * @addtogroup PAL - * @{ - */ - -#ifndef _PAL_LLD_H_ -#define _PAL_LLD_H_ - -#if HAL_USE_PAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Unsupported modes and specific modes */ -/*===========================================================================*/ - -#undef PAL_MODE_RESET -#undef PAL_MODE_UNCONNECTED -#undef PAL_MODE_INPUT -#undef PAL_MODE_INPUT_PULLUP -#undef PAL_MODE_INPUT_PULLDOWN -#undef PAL_MODE_INPUT_ANALOG -#undef PAL_MODE_OUTPUT_PUSHPULL -#undef PAL_MODE_OUTPUT_OPENDRAIN - -/** - * @name STM32-specific I/O mode flags - * @{ - */ -#define PAL_STM32_MODE_MASK (3U << 0U) -#define PAL_STM32_MODE_INPUT (0U << 0U) -#define PAL_STM32_MODE_OUTPUT (1U << 0U) -#define PAL_STM32_MODE_ALTERNATE (2U << 0U) -#define PAL_STM32_MODE_ANALOG (3U << 0U) - -#define PAL_STM32_OTYPE_MASK (1U << 2U) -#define PAL_STM32_OTYPE_PUSHPULL (0U << 2U) -#define PAL_STM32_OTYPE_OPENDRAIN (1U << 2U) - -#define PAL_STM32_OSPEED_MASK (3U << 3U) -#define PAL_STM32_OSPEED_LOWEST (0U << 3U) -#if defined(STM32F0XX) || defined(STM32F30X) || defined(STM32F37X) -#define PAL_STM32_OSPEED_MID (1U << 3U) -#else -#define PAL_STM32_OSPEED_MID1 (1U << 3U) -#define PAL_STM32_OSPEED_MID2 (2U << 3U) -#endif -#define PAL_STM32_OSPEED_HIGHEST (3U << 3U) - -#define PAL_STM32_PUPDR_MASK (3U << 5U) -#define PAL_STM32_PUPDR_FLOATING (0U << 5U) -#define PAL_STM32_PUPDR_PULLUP (1U << 5U) -#define PAL_STM32_PUPDR_PULLDOWN (2U << 5U) - -#define PAL_STM32_ALTERNATE_MASK (15U << 7U) -#define PAL_STM32_ALTERNATE(n) ((n) << 7U) - -/** - * @brief Alternate function. - * - * @param[in] n alternate function selector - */ -#define PAL_MODE_ALTERNATE(n) (PAL_STM32_MODE_ALTERNATE | \ - PAL_STM32_ALTERNATE(n)) -/** @} */ - -/** - * @name Standard I/O mode flags - * @{ - */ -/** - * @brief This mode is implemented as input. - */ -#define PAL_MODE_RESET PAL_STM32_MODE_INPUT - -/** - * @brief This mode is implemented as input with pull-up. - */ -#define PAL_MODE_UNCONNECTED PAL_MODE_INPUT_PULLUP - -/** - * @brief Regular input high-Z pad. - */ -#define PAL_MODE_INPUT PAL_STM32_MODE_INPUT - -/** - * @brief Input pad with weak pull up resistor. - */ -#define PAL_MODE_INPUT_PULLUP (PAL_STM32_MODE_INPUT | \ - PAL_STM32_PUPDR_PULLUP) - -/** - * @brief Input pad with weak pull down resistor. - */ -#define PAL_MODE_INPUT_PULLDOWN (PAL_STM32_MODE_INPUT | \ - PAL_STM32_PUPDR_PULLDOWN) - -/** - * @brief Analog input mode. - */ -#define PAL_MODE_INPUT_ANALOG PAL_STM32_MODE_ANALOG - -/** - * @brief Push-pull output pad. - */ -#define PAL_MODE_OUTPUT_PUSHPULL (PAL_STM32_MODE_OUTPUT | \ - PAL_STM32_OTYPE_PUSHPULL) - -/** - * @brief Open-drain output pad. - */ -#define PAL_MODE_OUTPUT_OPENDRAIN (PAL_STM32_MODE_OUTPUT | \ - PAL_STM32_OTYPE_OPENDRAIN) -/** @} */ - -/* Discarded definitions from the ST headers, the PAL driver uses its own - definitions in order to have an unified handling for all devices. - Unfortunately the ST headers have no uniform definitions for the same - objects across the various sub-families.*/ -#undef GPIOA -#undef GPIOB -#undef GPIOC -#undef GPIOD -#undef GPIOE -#undef GPIOF -#undef GPIOG -#undef GPIOH -#undef GPIOI -#undef GPIOJ -#undef GPIOK - -/** - * @name GPIO ports definitions - * @{ - */ -#define GPIOA ((stm32_gpio_t *)GPIOA_BASE) -#define GPIOB ((stm32_gpio_t *)GPIOB_BASE) -#define GPIOC ((stm32_gpio_t *)GPIOC_BASE) -#define GPIOD ((stm32_gpio_t *)GPIOD_BASE) -#define GPIOE ((stm32_gpio_t *)GPIOE_BASE) -#define GPIOF ((stm32_gpio_t *)GPIOF_BASE) -#define GPIOG ((stm32_gpio_t *)GPIOG_BASE) -#define GPIOH ((stm32_gpio_t *)GPIOH_BASE) -#define GPIOI ((stm32_gpio_t *)GPIOI_BASE) -#define GPIOJ ((stm32_gpio_t *)GPIOJ_BASE) -#define GPIOK ((stm32_gpio_t *)GPIOK_BASE) -/** @} */ - -/*===========================================================================*/ -/* I/O Ports Types and constants. */ -/*===========================================================================*/ - -/** - * @name Port related definitions - * @{ - */ -/** - * @brief Width, in bits, of an I/O port. - */ -#define PAL_IOPORTS_WIDTH 16 - -/** - * @brief Whole port mask. - * @details This macro specifies all the valid bits into a port. - */ -#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF) -/** @} */ - -/** - * @name Line handling macros - * @{ - */ -/** - * @brief Forms a line identifier. - * @details A port/pad pair are encoded into an @p ioline_t type. The encoding - * of this type is platform-dependent. - * @note In this driver the pad number is encoded in the lower 4 bits of - * the GPIO address which are guaranteed to be zero. - */ -#define PAL_LINE(port, pad) \ - ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad))) - -/** - * @brief Decodes a port identifier from a line identifier. - */ -#define PAL_PORT(line) \ - ((stm32_gpio_t *)(((uint32_t)(line)) & 0xFFFFFFF0U)) - -/** - * @brief Decodes a pad identifier from a line identifier. - */ -#define PAL_PAD(line) \ - ((uint32_t)((uint32_t)(line) & 0x0000000FU)) - -/** - * @brief Value identifying an invalid line. - */ -#define PAL_NOLINE 0U -/** @} */ - -/** - * @brief STM32 GPIO registers block. - */ -typedef struct { - - volatile uint32_t MODER; - volatile uint32_t OTYPER; - volatile uint32_t OSPEEDR; - volatile uint32_t PUPDR; - volatile uint32_t IDR; - volatile uint32_t ODR; - volatile union { - uint32_t W; - struct { - uint16_t set; - uint16_t clear; - } H; - } BSRR; - volatile uint32_t LCKR; - volatile uint32_t AFRL; - volatile uint32_t AFRH; - volatile uint32_t BRR; -} stm32_gpio_t; - -/** - * @brief GPIO port setup info. - */ -typedef struct { - /** Initial value for MODER register.*/ - uint32_t moder; - /** Initial value for OTYPER register.*/ - uint32_t otyper; - /** Initial value for OSPEEDR register.*/ - uint32_t ospeedr; - /** Initial value for PUPDR register.*/ - uint32_t pupdr; - /** Initial value for ODR register.*/ - uint32_t odr; - /** Initial value for AFRL register.*/ - uint32_t afrl; - /** Initial value for AFRH register.*/ - uint32_t afrh; -} stm32_gpio_setup_t; - -/** - * @brief STM32 GPIO static initializer. - * @details An instance of this structure must be passed to @p palInit() at - * system startup time in order to initialize the digital I/O - * subsystem. This represents only the initial setup, specific pads - * or whole ports can be reprogrammed at later time. - */ -typedef struct { -#if STM32_HAS_GPIOA || defined(__DOXYGEN__) - /** @brief Port A setup data.*/ - stm32_gpio_setup_t PAData; -#endif -#if STM32_HAS_GPIOB || defined(__DOXYGEN__) - /** @brief Port B setup data.*/ - stm32_gpio_setup_t PBData; -#endif -#if STM32_HAS_GPIOC || defined(__DOXYGEN__) - /** @brief Port C setup data.*/ - stm32_gpio_setup_t PCData; -#endif -#if STM32_HAS_GPIOD || defined(__DOXYGEN__) - /** @brief Port D setup data.*/ - stm32_gpio_setup_t PDData; -#endif -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) - /** @brief Port E setup data.*/ - stm32_gpio_setup_t PEData; -#endif -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) - /** @brief Port F setup data.*/ - stm32_gpio_setup_t PFData; -#endif -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) - /** @brief Port G setup data.*/ - stm32_gpio_setup_t PGData; -#endif -#if STM32_HAS_GPIOH || defined(__DOXYGEN__) - /** @brief Port H setup data.*/ - stm32_gpio_setup_t PHData; -#endif -#if STM32_HAS_GPIOI || defined(__DOXYGEN__) - /** @brief Port I setup data.*/ - stm32_gpio_setup_t PIData; -#endif -#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) - /** @brief Port I setup data.*/ - stm32_gpio_setup_t PJData; -#endif -#if STM32_HAS_GPIOK || defined(__DOXYGEN__) - /** @brief Port I setup data.*/ - stm32_gpio_setup_t PKData; -#endif -} PALConfig; - -/** - * @brief Type of digital I/O port sized unsigned integer. - */ -typedef uint32_t ioportmask_t; - -/** - * @brief Type of digital I/O modes. - */ -typedef uint32_t iomode_t; - -/** - * @brief Type of an I/O line. - */ -typedef uint32_t ioline_t; - -/** - * @brief Port Identifier. - * @details This type can be a scalar or some kind of pointer, do not make - * any assumption about it, use the provided macros when populating - * variables of this type. - */ -typedef stm32_gpio_t * ioportid_t; - -/*===========================================================================*/ -/* I/O Ports Identifiers. */ -/* The low level driver wraps the definitions already present in the STM32 */ -/* firmware library. */ -/*===========================================================================*/ - -/** - * @brief GPIO port A identifier. - */ -#if STM32_HAS_GPIOA || defined(__DOXYGEN__) -#define IOPORT1 GPIOA -#endif - -/** - * @brief GPIO port B identifier. - */ -#if STM32_HAS_GPIOB || defined(__DOXYGEN__) -#define IOPORT2 GPIOB -#endif - -/** - * @brief GPIO port C identifier. - */ -#if STM32_HAS_GPIOC || defined(__DOXYGEN__) -#define IOPORT3 GPIOC -#endif - -/** - * @brief GPIO port D identifier. - */ -#if STM32_HAS_GPIOD || defined(__DOXYGEN__) -#define IOPORT4 GPIOD -#endif - -/** - * @brief GPIO port E identifier. - */ -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) -#define IOPORT5 GPIOE -#endif - -/** - * @brief GPIO port F identifier. - */ -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) -#define IOPORT6 GPIOF -#endif - -/** - * @brief GPIO port G identifier. - */ -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) -#define IOPORT7 GPIOG -#endif - -/** - * @brief GPIO port H identifier. - */ -#if STM32_HAS_GPIOH || defined(__DOXYGEN__) -#define IOPORT8 GPIOH -#endif - -/** - * @brief GPIO port I identifier. - */ -#if STM32_HAS_GPIOI || defined(__DOXYGEN__) -#define IOPORT9 GPIOI -#endif - -/** - * @brief GPIO port J identifier. - */ -#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) -#define IOPORT10 GPIOJ -#endif - -/** - * @brief GPIO port K identifier. - */ -#if STM32_HAS_GPIOK || defined(__DOXYGEN__) -#define IOPORT11 GPIOK -#endif - -/*===========================================================================*/ -/* Implementation, some of the following macros could be implemented as */ -/* functions, if so please put them in pal_lld.c. */ -/*===========================================================================*/ - -/** - * @brief GPIO ports subsystem initialization. - * - * @notapi - */ -#define pal_lld_init(config) _pal_lld_init(config) - -/** - * @brief Reads an I/O port. - * @details This function is implemented by reading the GPIO IDR register, the - * implementation has no side effects. - * @note This function is not meant to be invoked directly by the application - * code. - * - * @param[in] port port identifier - * @return The port bits. - * - * @notapi - */ -#define pal_lld_readport(port) ((port)->IDR) - -/** - * @brief Reads the output latch. - * @details This function is implemented by reading the GPIO ODR register, the - * implementation has no side effects. - * @note This function is not meant to be invoked directly by the application - * code. - * - * @param[in] port port identifier - * @return The latched logical states. - * - * @notapi - */ -#define pal_lld_readlatch(port) ((port)->ODR) - -/** - * @brief Writes on a I/O port. - * @details This function is implemented by writing the GPIO ODR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] bits bits to be written on the specified port - * - * @notapi - */ -#define pal_lld_writeport(port, bits) ((port)->ODR = (bits)) - -/** - * @brief Sets a bits mask on a I/O port. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] bits bits to be ORed on the specified port - * - * @notapi - */ -#define pal_lld_setport(port, bits) ((port)->BSRR.H.set = (uint16_t)(bits)) - -/** - * @brief Clears a bits mask on a I/O port. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] bits bits to be cleared on the specified port - * - * @notapi - */ -#define pal_lld_clearport(port, bits) ((port)->BSRR.H.clear = (uint16_t)(bits)) - -/** - * @brief Writes a group of bits. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset the group bit offset within the port - * @param[in] bits bits to be written. Values exceeding the group - * width are masked. - * - * @notapi - */ -#define pal_lld_writegroup(port, mask, offset, bits) \ - ((port)->BSRR.W = ((~(bits) & (mask)) << (16U + (offset))) | \ - (((bits) & (mask)) << (offset))) - -/** - * @brief Pads group mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset group bit offset within the port - * @param[in] mode group mode - * - * @notapi - */ -#define pal_lld_setgroupmode(port, mask, offset, mode) \ - _pal_lld_setgroupmode(port, mask << offset, mode) - -/** - * @brief Writes a logical state on an output pad. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @param[in] bit logical value, the value must be @p PAL_LOW or - * @p PAL_HIGH - * - * @notapi - */ -#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) - -extern const PALConfig pal_default_config; - -#ifdef __cplusplus -extern "C" { -#endif - void _pal_lld_init(const PALConfig *config); - void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_PAL */ - -#endif /* _PAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.c b/os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.c new file mode 100644 index 000000000..9078674f4 --- /dev/null +++ b/os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.c @@ -0,0 +1,199 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/GPIOv3/pal_lld.c + * @brief STM32 PAL low level driver code. + * + * @addtogroup PAL + * @{ + */ + +#include "hal.h" + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if defined(STM32L4XX) +#define AHB1_EN_MASK STM32_GPIO_EN_MASK +#define AHB1_LPEN_MASK 0 + +#else +#error "missing or unsupported platform for GPIOv3 PAL driver" +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void initgpio(stm32_gpio_t *gpiop, const stm32_gpio_setup_t *config) { + + gpiop->OTYPER = config->otyper; + gpiop->ASCR = config->ascr; + gpiop->OSPEEDR = config->ospeedr; + gpiop->PUPDR = config->pupdr; + gpiop->ODR = config->odr; + gpiop->AFRL = config->afrl; + gpiop->AFRH = config->afrh; + gpiop->MODER = config->moder; + gpiop->LOCKR = config->lockr; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief STM32 I/O ports configuration. + * @details Ports A-D(E, F, G, H) clocks enabled. + * + * @param[in] config the STM32 ports configuration + * + * @notapi + */ +void _pal_lld_init(const PALConfig *config) { + + /* + * Enables the GPIO related clocks. + */ +#if defined(STM32L4XX) + RCC->AHB2ENR |= AHB1_EN_MASK; +#endif + + /* + * Initial GPIO setup. + */ +#if STM32_HAS_GPIOA + initgpio(GPIOA, &config->PAData); +#endif +#if STM32_HAS_GPIOB + initgpio(GPIOB, &config->PBData); +#endif +#if STM32_HAS_GPIOC + initgpio(GPIOC, &config->PCData); +#endif +#if STM32_HAS_GPIOD + initgpio(GPIOD, &config->PDData); +#endif +#if STM32_HAS_GPIOE + initgpio(GPIOE, &config->PEData); +#endif +#if STM32_HAS_GPIOF + initgpio(GPIOF, &config->PFData); +#endif +#if STM32_HAS_GPIOG + initgpio(GPIOG, &config->PGData); +#endif +#if STM32_HAS_GPIOH + initgpio(GPIOH, &config->PHData); +#endif +#if STM32_HAS_GPIOI + initgpio(GPIOI, &config->PIData); +#endif +#if STM32_HAS_GPIOJ + initgpio(GPIOJ, &config->PJData); +#endif +#if STM32_HAS_GPIOK + initgpio(GPIOK, &config->PKData); +#endif +} + +/** + * @brief Pads mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * @note @p PAL_MODE_UNCONNECTED is implemented as push pull at minimum + * speed. + * + * @param[in] port the port identifier + * @param[in] mask the group mask + * @param[in] mode the mode + * + * @notapi + */ +void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode) { + + uint32_t moder = (mode & PAL_STM32_MODE_MASK) >> 0; + uint32_t otyper = (mode & PAL_STM32_OTYPE_MASK) >> 2; + uint32_t ospeedr = (mode & PAL_STM32_OSPEED_MASK) >> 3; + uint32_t pupdr = (mode & PAL_STM32_PUPDR_MASK) >> 5; + uint32_t altr = (mode & PAL_STM32_ALTERNATE_MASK) >> 7; + uint32_t ascr = (mode & PAL_STM32_ASCR_MASK) >> 11; + uint32_t lockr = (mode & PAL_STM32_LOCKR_MASK) >> 12; + uint32_t bit = 0; + while (true) { + if ((mask & 1) != 0) { + uint32_t altrmask, m1, m2, m4; + + altrmask = altr << ((bit & 7) * 4); + m1 = 1 << bit; + m2 = 3 << (bit * 2); + m4 = 15 << ((bit & 7) * 4); + port->OTYPER = (port->OTYPER & ~m1) | otyper; + port->ASCR = (port->ASCR & ~m1) | ascr; + port->OSPEEDR = (port->OSPEEDR & ~m2) | ospeedr; + port->PUPDR = (port->PUPDR & ~m2) | pupdr; + if (moder == PAL_STM32_MODE_ALTERNATE) { + /* If going in alternate mode then the alternate number is set + before switching mode in order to avoid glitches.*/ + if (bit < 8) + port->AFRL = (port->AFRL & ~m4) | altrmask; + else + port->AFRH = (port->AFRH & ~m4) | altrmask; + port->MODER = (port->MODER & ~m2) | moder; + } + else { + /* If going into a non-alternate mode then the mode is switched + before setting the alternate mode in order to avoid glitches.*/ + port->MODER = (port->MODER & ~m2) | moder; + if (bit < 8) + port->AFRL = (port->AFRL & ~m4) | altrmask; + else + port->AFRH = (port->AFRH & ~m4) | altrmask; + } + port->LOCKR = (port->LOCKR & ~m1) | lockr; + } + mask >>= 1; + if (!mask) + return; + otyper <<= 1; + ospeedr <<= 2; + pupdr <<= 2; + moder <<= 2; + bit++; + } +} + +#endif /* HAL_USE_PAL */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.h b/os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.h new file mode 100644 index 000000000..0e0b5934a --- /dev/null +++ b/os/hal/ports/STM32/LLD/GPIOv3/hal_pal_lld.h @@ -0,0 +1,571 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/GPIOv3/pal_lld.h + * @brief STM32 PAL low level driver header. + * + * @addtogroup PAL + * @{ + */ + +#ifndef _PAL_LLD_H_ +#define _PAL_LLD_H_ + +#if HAL_USE_PAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Unsupported modes and specific modes */ +/*===========================================================================*/ + +#undef PAL_MODE_RESET +#undef PAL_MODE_UNCONNECTED +#undef PAL_MODE_INPUT +#undef PAL_MODE_INPUT_PULLUP +#undef PAL_MODE_INPUT_PULLDOWN +#undef PAL_MODE_INPUT_ANALOG +#undef PAL_MODE_OUTPUT_PUSHPULL +#undef PAL_MODE_OUTPUT_OPENDRAIN + +/** + * @name STM32-specific I/O mode flags + * @{ + */ +#define PAL_STM32_MODE_MASK (3U << 0U) +#define PAL_STM32_MODE_INPUT (0U << 0U) +#define PAL_STM32_MODE_OUTPUT (1U << 0U) +#define PAL_STM32_MODE_ALTERNATE (2U << 0U) +#define PAL_STM32_MODE_ANALOG (3U << 0U) + +#define PAL_STM32_OTYPE_MASK (1U << 2U) +#define PAL_STM32_OTYPE_PUSHPULL (0U << 2U) +#define PAL_STM32_OTYPE_OPENDRAIN (1U << 2U) + +#define PAL_STM32_OSPEED_MASK (3U << 3U) +#define PAL_STM32_OSPEED_LOW (0U << 3U) +#define PAL_STM32_OSPEED_MEDIUM (1U << 3U) +#define PAL_STM32_OSPEED_FAST (2U << 3U) +#define PAL_STM32_OSPEED_HIGH (3U << 3U) + +#define PAL_STM32_PUPDR_MASK (3U << 5U) +#define PAL_STM32_PUPDR_FLOATING (0U << 5U) +#define PAL_STM32_PUPDR_PULLUP (1U << 5U) +#define PAL_STM32_PUPDR_PULLDOWN (2U << 5U) + +#define PAL_STM32_ALTERNATE_MASK (15U << 7U) +#define PAL_STM32_ALTERNATE(n) ((n) << 7U) + +#define PAL_STM32_ASCR_MASK (1U << 11U) +#define PAL_STM32_ASCR_OFF (0U << 11U) +#define PAL_STM32_ASCR_ON (1U << 11U) + +#define PAL_STM32_LOCKR_MASK (1U << 12U) +#define PAL_STM32_LOCKR_OFF (0U << 12U) +#define PAL_STM32_LOCKR_ON (1U << 12U) + +/** + * @brief Alternate function. + * + * @param[in] n alternate function selector + */ +#define PAL_MODE_ALTERNATE(n) (PAL_STM32_MODE_ALTERNATE | \ + PAL_STM32_ALTERNATE(n)) +/** @} */ + +/** + * @name Standard I/O mode flags + * @{ + */ +/** + * @brief Implemented as input. + */ +#define PAL_MODE_RESET PAL_STM32_MODE_INPUT + +/** + * @brief Implemented as analog with analog switch disabled and lock. + */ +#define PAL_MODE_UNCONNECTED (PAL_STM32_MODE_ANALOG | \ + PAL_STM32_ASCR_OFF | \ + PAL_STM32_LOCKR_ON) + +/** + * @brief Regular input high-Z pad. + */ +#define PAL_MODE_INPUT PAL_STM32_MODE_INPUT + +/** + * @brief Input pad with weak pull up resistor. + */ +#define PAL_MODE_INPUT_PULLUP (PAL_STM32_MODE_INPUT | \ + PAL_STM32_PUPDR_PULLUP) + +/** + * @brief Input pad with weak pull down resistor. + */ +#define PAL_MODE_INPUT_PULLDOWN (PAL_STM32_MODE_INPUT | \ + PAL_STM32_PUPDR_PULLDOWN) + +/** + * @brief Analog input mode. + */ +#define PAL_MODE_INPUT_ANALOG (PAL_STM32_MODE_ANALOG | \ + PAL_STM32_ASCR_ON) + +/** + * @brief Push-pull output pad. + */ +#define PAL_MODE_OUTPUT_PUSHPULL (PAL_STM32_MODE_OUTPUT | \ + PAL_STM32_OTYPE_PUSHPULL) + +/** + * @brief Open-drain output pad. + */ +#define PAL_MODE_OUTPUT_OPENDRAIN (PAL_STM32_MODE_OUTPUT | \ + PAL_STM32_OTYPE_OPENDRAIN) +/** @} */ + +/* Discarded definitions from the ST headers, the PAL driver uses its own + definitions in order to have an unified handling for all devices. + Unfortunately the ST headers have no uniform definitions for the same + objects across the various sub-families.*/ +#undef GPIOA +#undef GPIOB +#undef GPIOC +#undef GPIOD +#undef GPIOE +#undef GPIOF +#undef GPIOG +#undef GPIOH +#undef GPIOI +#undef GPIOJ +#undef GPIOK + +/** + * @name GPIO ports definitions + * @{ + */ +#define GPIOA ((stm32_gpio_t *)GPIOA_BASE) +#define GPIOB ((stm32_gpio_t *)GPIOB_BASE) +#define GPIOC ((stm32_gpio_t *)GPIOC_BASE) +#define GPIOD ((stm32_gpio_t *)GPIOD_BASE) +#define GPIOE ((stm32_gpio_t *)GPIOE_BASE) +#define GPIOF ((stm32_gpio_t *)GPIOF_BASE) +#define GPIOG ((stm32_gpio_t *)GPIOG_BASE) +#define GPIOH ((stm32_gpio_t *)GPIOH_BASE) +#define GPIOI ((stm32_gpio_t *)GPIOI_BASE) +#define GPIOJ ((stm32_gpio_t *)GPIOJ_BASE) +#define GPIOK ((stm32_gpio_t *)GPIOK_BASE) +/** @} */ + +/*===========================================================================*/ +/* I/O Ports Types and constants. */ +/*===========================================================================*/ + +/** + * @name Port related definitions + * @{ + */ +/** + * @brief Width, in bits, of an I/O port. + */ +#define PAL_IOPORTS_WIDTH 16 + +/** + * @brief Whole port mask. + * @details This macro specifies all the valid bits into a port. + */ +#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF) +/** @} */ + +/** + * @name Line handling macros + * @{ + */ +/** + * @brief Forms a line identifier. + * @details A port/pad pair are encoded into an @p ioline_t type. The encoding + * of this type is platform-dependent. + * @note In this driver the pad number is encoded in the lower 4 bits of + * the GPIO address which are guaranteed to be zero. + */ +#define PAL_LINE(port, pad) \ + ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad))) + +/** + * @brief Decodes a port identifier from a line identifier. + */ +#define PAL_PORT(line) \ + ((stm32_gpio_t *)(((uint32_t)(line)) & 0xFFFFFFF0U)) + +/** + * @brief Decodes a pad identifier from a line identifier. + */ +#define PAL_PAD(line) \ + ((uint32_t)((uint32_t)(line) & 0x0000000FU)) + +/** + * @brief Value identifying an invalid line. + */ +#define PAL_NOLINE 0U +/** @} */ + +/** + * @brief STM32 GPIO registers block. + */ +typedef struct { + + volatile uint32_t MODER; + volatile uint32_t OTYPER; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile union { + uint32_t W; + struct { + uint16_t set; + uint16_t clear; + } H; + } BSRR; + volatile uint32_t LOCKR; + volatile uint32_t AFRL; + volatile uint32_t AFRH; + volatile uint32_t BRR; + volatile uint32_t ASCR; +} stm32_gpio_t; + +/** + * @brief GPIO port setup info. + */ +typedef struct { + /** Initial value for MODER register.*/ + uint32_t moder; + /** Initial value for OTYPER register.*/ + uint32_t otyper; + /** Initial value for OSPEEDR register.*/ + uint32_t ospeedr; + /** Initial value for PUPDR register.*/ + uint32_t pupdr; + /** Initial value for ODR register.*/ + uint32_t odr; + /** Initial value for AFRL register.*/ + uint32_t afrl; + /** Initial value for AFRH register.*/ + uint32_t afrh; + /** Initial value for ASCR register.*/ + uint32_t ascr; + /** Initial value for LOCKR register.*/ + uint32_t lockr; +} stm32_gpio_setup_t; + +/** + * @brief STM32 GPIO static initializer. + * @details An instance of this structure must be passed to @p palInit() at + * system startup time in order to initialize the digital I/O + * subsystem. This represents only the initial setup, specific pads + * or whole ports can be reprogrammed at later time. + */ +typedef struct { +#if STM32_HAS_GPIOA || defined(__DOXYGEN__) + /** @brief Port A setup data.*/ + stm32_gpio_setup_t PAData; +#endif +#if STM32_HAS_GPIOB || defined(__DOXYGEN__) + /** @brief Port B setup data.*/ + stm32_gpio_setup_t PBData; +#endif +#if STM32_HAS_GPIOC || defined(__DOXYGEN__) + /** @brief Port C setup data.*/ + stm32_gpio_setup_t PCData; +#endif +#if STM32_HAS_GPIOD || defined(__DOXYGEN__) + /** @brief Port D setup data.*/ + stm32_gpio_setup_t PDData; +#endif +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) + /** @brief Port E setup data.*/ + stm32_gpio_setup_t PEData; +#endif +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) + /** @brief Port F setup data.*/ + stm32_gpio_setup_t PFData; +#endif +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) + /** @brief Port G setup data.*/ + stm32_gpio_setup_t PGData; +#endif +#if STM32_HAS_GPIOH || defined(__DOXYGEN__) + /** @brief Port H setup data.*/ + stm32_gpio_setup_t PHData; +#endif +#if STM32_HAS_GPIOI || defined(__DOXYGEN__) + /** @brief Port I setup data.*/ + stm32_gpio_setup_t PIData; +#endif +#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) + /** @brief Port I setup data.*/ + stm32_gpio_setup_t PJData; +#endif +#if STM32_HAS_GPIOK || defined(__DOXYGEN__) + /** @brief Port I setup data.*/ + stm32_gpio_setup_t PKData; +#endif +} PALConfig; + +/** + * @brief Type of digital I/O port sized unsigned integer. + */ +typedef uint32_t ioportmask_t; + +/** + * @brief Type of digital I/O modes. + */ +typedef uint32_t iomode_t; + +/** + * @brief Type of an I/O line. + */ +typedef uint32_t ioline_t; + +/** + * @brief Port Identifier. + * @details This type can be a scalar or some kind of pointer, do not make + * any assumption about it, use the provided macros when populating + * variables of this type. + */ +typedef stm32_gpio_t * ioportid_t; + +/*===========================================================================*/ +/* I/O Ports Identifiers. */ +/* The low level driver wraps the definitions already present in the STM32 */ +/* firmware library. */ +/*===========================================================================*/ + +/** + * @brief GPIO port A identifier. + */ +#if STM32_HAS_GPIOA || defined(__DOXYGEN__) +#define IOPORT1 GPIOA +#endif + +/** + * @brief GPIO port B identifier. + */ +#if STM32_HAS_GPIOB || defined(__DOXYGEN__) +#define IOPORT2 GPIOB +#endif + +/** + * @brief GPIO port C identifier. + */ +#if STM32_HAS_GPIOC || defined(__DOXYGEN__) +#define IOPORT3 GPIOC +#endif + +/** + * @brief GPIO port D identifier. + */ +#if STM32_HAS_GPIOD || defined(__DOXYGEN__) +#define IOPORT4 GPIOD +#endif + +/** + * @brief GPIO port E identifier. + */ +#if STM32_HAS_GPIOE || defined(__DOXYGEN__) +#define IOPORT5 GPIOE +#endif + +/** + * @brief GPIO port F identifier. + */ +#if STM32_HAS_GPIOF || defined(__DOXYGEN__) +#define IOPORT6 GPIOF +#endif + +/** + * @brief GPIO port G identifier. + */ +#if STM32_HAS_GPIOG || defined(__DOXYGEN__) +#define IOPORT7 GPIOG +#endif + +/** + * @brief GPIO port H identifier. + */ +#if STM32_HAS_GPIOH || defined(__DOXYGEN__) +#define IOPORT8 GPIOH +#endif + +/** + * @brief GPIO port I identifier. + */ +#if STM32_HAS_GPIOI || defined(__DOXYGEN__) +#define IOPORT9 GPIOI +#endif + +/** + * @brief GPIO port J identifier. + */ +#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) +#define IOPORT10 GPIOJ +#endif + +/** + * @brief GPIO port K identifier. + */ +#if STM32_HAS_GPIOK || defined(__DOXYGEN__) +#define IOPORT11 GPIOK +#endif + +/*===========================================================================*/ +/* Implementation, some of the following macros could be implemented as */ +/* functions, if so please put them in pal_lld.c. */ +/*===========================================================================*/ + +/** + * @brief GPIO ports subsystem initialization. + * + * @notapi + */ +#define pal_lld_init(config) _pal_lld_init(config) + +/** + * @brief Reads an I/O port. + * @details This function is implemented by reading the GPIO IDR register, the + * implementation has no side effects. + * @note This function is not meant to be invoked directly by the application + * code. + * + * @param[in] port port identifier + * @return The port bits. + * + * @notapi + */ +#define pal_lld_readport(port) ((port)->IDR) + +/** + * @brief Reads the output latch. + * @details This function is implemented by reading the GPIO ODR register, the + * implementation has no side effects. + * @note This function is not meant to be invoked directly by the application + * code. + * + * @param[in] port port identifier + * @return The latched logical states. + * + * @notapi + */ +#define pal_lld_readlatch(port) ((port)->ODR) + +/** + * @brief Writes on a I/O port. + * @details This function is implemented by writing the GPIO ODR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] bits bits to be written on the specified port + * + * @notapi + */ +#define pal_lld_writeport(port, bits) ((port)->ODR = (bits)) + +/** + * @brief Sets a bits mask on a I/O port. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] bits bits to be ORed on the specified port + * + * @notapi + */ +#define pal_lld_setport(port, bits) ((port)->BSRR.H.set = (uint16_t)(bits)) + +/** + * @brief Clears a bits mask on a I/O port. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] bits bits to be cleared on the specified port + * + * @notapi + */ +#define pal_lld_clearport(port, bits) ((port)->BSRR.H.clear = (uint16_t)(bits)) + +/** + * @brief Writes a group of bits. + * @details This function is implemented by writing the GPIO BSRR register, the + * implementation has no side effects. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset the group bit offset within the port + * @param[in] bits bits to be written. Values exceeding the group + * width are masked. + * + * @notapi + */ +#define pal_lld_writegroup(port, mask, offset, bits) \ + ((port)->BSRR.W = ((~(bits) & (mask)) << (16U + (offset))) | \ + (((bits) & (mask)) << (offset))) + +/** + * @brief Pads group mode setup. + * @details This function programs a pads group belonging to the same port + * with the specified mode. + * + * @param[in] port port identifier + * @param[in] mask group mask + * @param[in] offset group bit offset within the port + * @param[in] mode group mode + * + * @notapi + */ +#define pal_lld_setgroupmode(port, mask, offset, mode) \ + _pal_lld_setgroupmode(port, mask << offset, mode) + +/** + * @brief Writes a logical state on an output pad. + * + * @param[in] port port identifier + * @param[in] pad pad number within the port + * @param[in] bit logical value, the value must be @p PAL_LOW or + * @p PAL_HIGH + * + * @notapi + */ +#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) + +extern const PALConfig pal_default_config; + +#ifdef __cplusplus +extern "C" { +#endif + void _pal_lld_init(const PALConfig *config); + void _pal_lld_setgroupmode(ioportid_t port, + ioportmask_t mask, + iomode_t mode); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PAL */ + +#endif /* _PAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv3/pal_lld.c b/os/hal/ports/STM32/LLD/GPIOv3/pal_lld.c deleted file mode 100644 index 9078674f4..000000000 --- a/os/hal/ports/STM32/LLD/GPIOv3/pal_lld.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/GPIOv3/pal_lld.c - * @brief STM32 PAL low level driver code. - * - * @addtogroup PAL - * @{ - */ - -#include "hal.h" - -#if HAL_USE_PAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if defined(STM32L4XX) -#define AHB1_EN_MASK STM32_GPIO_EN_MASK -#define AHB1_LPEN_MASK 0 - -#else -#error "missing or unsupported platform for GPIOv3 PAL driver" -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void initgpio(stm32_gpio_t *gpiop, const stm32_gpio_setup_t *config) { - - gpiop->OTYPER = config->otyper; - gpiop->ASCR = config->ascr; - gpiop->OSPEEDR = config->ospeedr; - gpiop->PUPDR = config->pupdr; - gpiop->ODR = config->odr; - gpiop->AFRL = config->afrl; - gpiop->AFRH = config->afrh; - gpiop->MODER = config->moder; - gpiop->LOCKR = config->lockr; -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief STM32 I/O ports configuration. - * @details Ports A-D(E, F, G, H) clocks enabled. - * - * @param[in] config the STM32 ports configuration - * - * @notapi - */ -void _pal_lld_init(const PALConfig *config) { - - /* - * Enables the GPIO related clocks. - */ -#if defined(STM32L4XX) - RCC->AHB2ENR |= AHB1_EN_MASK; -#endif - - /* - * Initial GPIO setup. - */ -#if STM32_HAS_GPIOA - initgpio(GPIOA, &config->PAData); -#endif -#if STM32_HAS_GPIOB - initgpio(GPIOB, &config->PBData); -#endif -#if STM32_HAS_GPIOC - initgpio(GPIOC, &config->PCData); -#endif -#if STM32_HAS_GPIOD - initgpio(GPIOD, &config->PDData); -#endif -#if STM32_HAS_GPIOE - initgpio(GPIOE, &config->PEData); -#endif -#if STM32_HAS_GPIOF - initgpio(GPIOF, &config->PFData); -#endif -#if STM32_HAS_GPIOG - initgpio(GPIOG, &config->PGData); -#endif -#if STM32_HAS_GPIOH - initgpio(GPIOH, &config->PHData); -#endif -#if STM32_HAS_GPIOI - initgpio(GPIOI, &config->PIData); -#endif -#if STM32_HAS_GPIOJ - initgpio(GPIOJ, &config->PJData); -#endif -#if STM32_HAS_GPIOK - initgpio(GPIOK, &config->PKData); -#endif -} - -/** - * @brief Pads mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * @note @p PAL_MODE_UNCONNECTED is implemented as push pull at minimum - * speed. - * - * @param[in] port the port identifier - * @param[in] mask the group mask - * @param[in] mode the mode - * - * @notapi - */ -void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode) { - - uint32_t moder = (mode & PAL_STM32_MODE_MASK) >> 0; - uint32_t otyper = (mode & PAL_STM32_OTYPE_MASK) >> 2; - uint32_t ospeedr = (mode & PAL_STM32_OSPEED_MASK) >> 3; - uint32_t pupdr = (mode & PAL_STM32_PUPDR_MASK) >> 5; - uint32_t altr = (mode & PAL_STM32_ALTERNATE_MASK) >> 7; - uint32_t ascr = (mode & PAL_STM32_ASCR_MASK) >> 11; - uint32_t lockr = (mode & PAL_STM32_LOCKR_MASK) >> 12; - uint32_t bit = 0; - while (true) { - if ((mask & 1) != 0) { - uint32_t altrmask, m1, m2, m4; - - altrmask = altr << ((bit & 7) * 4); - m1 = 1 << bit; - m2 = 3 << (bit * 2); - m4 = 15 << ((bit & 7) * 4); - port->OTYPER = (port->OTYPER & ~m1) | otyper; - port->ASCR = (port->ASCR & ~m1) | ascr; - port->OSPEEDR = (port->OSPEEDR & ~m2) | ospeedr; - port->PUPDR = (port->PUPDR & ~m2) | pupdr; - if (moder == PAL_STM32_MODE_ALTERNATE) { - /* If going in alternate mode then the alternate number is set - before switching mode in order to avoid glitches.*/ - if (bit < 8) - port->AFRL = (port->AFRL & ~m4) | altrmask; - else - port->AFRH = (port->AFRH & ~m4) | altrmask; - port->MODER = (port->MODER & ~m2) | moder; - } - else { - /* If going into a non-alternate mode then the mode is switched - before setting the alternate mode in order to avoid glitches.*/ - port->MODER = (port->MODER & ~m2) | moder; - if (bit < 8) - port->AFRL = (port->AFRL & ~m4) | altrmask; - else - port->AFRH = (port->AFRH & ~m4) | altrmask; - } - port->LOCKR = (port->LOCKR & ~m1) | lockr; - } - mask >>= 1; - if (!mask) - return; - otyper <<= 1; - ospeedr <<= 2; - pupdr <<= 2; - moder <<= 2; - bit++; - } -} - -#endif /* HAL_USE_PAL */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/GPIOv3/pal_lld.h b/os/hal/ports/STM32/LLD/GPIOv3/pal_lld.h deleted file mode 100644 index 0e0b5934a..000000000 --- a/os/hal/ports/STM32/LLD/GPIOv3/pal_lld.h +++ /dev/null @@ -1,571 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/GPIOv3/pal_lld.h - * @brief STM32 PAL low level driver header. - * - * @addtogroup PAL - * @{ - */ - -#ifndef _PAL_LLD_H_ -#define _PAL_LLD_H_ - -#if HAL_USE_PAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Unsupported modes and specific modes */ -/*===========================================================================*/ - -#undef PAL_MODE_RESET -#undef PAL_MODE_UNCONNECTED -#undef PAL_MODE_INPUT -#undef PAL_MODE_INPUT_PULLUP -#undef PAL_MODE_INPUT_PULLDOWN -#undef PAL_MODE_INPUT_ANALOG -#undef PAL_MODE_OUTPUT_PUSHPULL -#undef PAL_MODE_OUTPUT_OPENDRAIN - -/** - * @name STM32-specific I/O mode flags - * @{ - */ -#define PAL_STM32_MODE_MASK (3U << 0U) -#define PAL_STM32_MODE_INPUT (0U << 0U) -#define PAL_STM32_MODE_OUTPUT (1U << 0U) -#define PAL_STM32_MODE_ALTERNATE (2U << 0U) -#define PAL_STM32_MODE_ANALOG (3U << 0U) - -#define PAL_STM32_OTYPE_MASK (1U << 2U) -#define PAL_STM32_OTYPE_PUSHPULL (0U << 2U) -#define PAL_STM32_OTYPE_OPENDRAIN (1U << 2U) - -#define PAL_STM32_OSPEED_MASK (3U << 3U) -#define PAL_STM32_OSPEED_LOW (0U << 3U) -#define PAL_STM32_OSPEED_MEDIUM (1U << 3U) -#define PAL_STM32_OSPEED_FAST (2U << 3U) -#define PAL_STM32_OSPEED_HIGH (3U << 3U) - -#define PAL_STM32_PUPDR_MASK (3U << 5U) -#define PAL_STM32_PUPDR_FLOATING (0U << 5U) -#define PAL_STM32_PUPDR_PULLUP (1U << 5U) -#define PAL_STM32_PUPDR_PULLDOWN (2U << 5U) - -#define PAL_STM32_ALTERNATE_MASK (15U << 7U) -#define PAL_STM32_ALTERNATE(n) ((n) << 7U) - -#define PAL_STM32_ASCR_MASK (1U << 11U) -#define PAL_STM32_ASCR_OFF (0U << 11U) -#define PAL_STM32_ASCR_ON (1U << 11U) - -#define PAL_STM32_LOCKR_MASK (1U << 12U) -#define PAL_STM32_LOCKR_OFF (0U << 12U) -#define PAL_STM32_LOCKR_ON (1U << 12U) - -/** - * @brief Alternate function. - * - * @param[in] n alternate function selector - */ -#define PAL_MODE_ALTERNATE(n) (PAL_STM32_MODE_ALTERNATE | \ - PAL_STM32_ALTERNATE(n)) -/** @} */ - -/** - * @name Standard I/O mode flags - * @{ - */ -/** - * @brief Implemented as input. - */ -#define PAL_MODE_RESET PAL_STM32_MODE_INPUT - -/** - * @brief Implemented as analog with analog switch disabled and lock. - */ -#define PAL_MODE_UNCONNECTED (PAL_STM32_MODE_ANALOG | \ - PAL_STM32_ASCR_OFF | \ - PAL_STM32_LOCKR_ON) - -/** - * @brief Regular input high-Z pad. - */ -#define PAL_MODE_INPUT PAL_STM32_MODE_INPUT - -/** - * @brief Input pad with weak pull up resistor. - */ -#define PAL_MODE_INPUT_PULLUP (PAL_STM32_MODE_INPUT | \ - PAL_STM32_PUPDR_PULLUP) - -/** - * @brief Input pad with weak pull down resistor. - */ -#define PAL_MODE_INPUT_PULLDOWN (PAL_STM32_MODE_INPUT | \ - PAL_STM32_PUPDR_PULLDOWN) - -/** - * @brief Analog input mode. - */ -#define PAL_MODE_INPUT_ANALOG (PAL_STM32_MODE_ANALOG | \ - PAL_STM32_ASCR_ON) - -/** - * @brief Push-pull output pad. - */ -#define PAL_MODE_OUTPUT_PUSHPULL (PAL_STM32_MODE_OUTPUT | \ - PAL_STM32_OTYPE_PUSHPULL) - -/** - * @brief Open-drain output pad. - */ -#define PAL_MODE_OUTPUT_OPENDRAIN (PAL_STM32_MODE_OUTPUT | \ - PAL_STM32_OTYPE_OPENDRAIN) -/** @} */ - -/* Discarded definitions from the ST headers, the PAL driver uses its own - definitions in order to have an unified handling for all devices. - Unfortunately the ST headers have no uniform definitions for the same - objects across the various sub-families.*/ -#undef GPIOA -#undef GPIOB -#undef GPIOC -#undef GPIOD -#undef GPIOE -#undef GPIOF -#undef GPIOG -#undef GPIOH -#undef GPIOI -#undef GPIOJ -#undef GPIOK - -/** - * @name GPIO ports definitions - * @{ - */ -#define GPIOA ((stm32_gpio_t *)GPIOA_BASE) -#define GPIOB ((stm32_gpio_t *)GPIOB_BASE) -#define GPIOC ((stm32_gpio_t *)GPIOC_BASE) -#define GPIOD ((stm32_gpio_t *)GPIOD_BASE) -#define GPIOE ((stm32_gpio_t *)GPIOE_BASE) -#define GPIOF ((stm32_gpio_t *)GPIOF_BASE) -#define GPIOG ((stm32_gpio_t *)GPIOG_BASE) -#define GPIOH ((stm32_gpio_t *)GPIOH_BASE) -#define GPIOI ((stm32_gpio_t *)GPIOI_BASE) -#define GPIOJ ((stm32_gpio_t *)GPIOJ_BASE) -#define GPIOK ((stm32_gpio_t *)GPIOK_BASE) -/** @} */ - -/*===========================================================================*/ -/* I/O Ports Types and constants. */ -/*===========================================================================*/ - -/** - * @name Port related definitions - * @{ - */ -/** - * @brief Width, in bits, of an I/O port. - */ -#define PAL_IOPORTS_WIDTH 16 - -/** - * @brief Whole port mask. - * @details This macro specifies all the valid bits into a port. - */ -#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF) -/** @} */ - -/** - * @name Line handling macros - * @{ - */ -/** - * @brief Forms a line identifier. - * @details A port/pad pair are encoded into an @p ioline_t type. The encoding - * of this type is platform-dependent. - * @note In this driver the pad number is encoded in the lower 4 bits of - * the GPIO address which are guaranteed to be zero. - */ -#define PAL_LINE(port, pad) \ - ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad))) - -/** - * @brief Decodes a port identifier from a line identifier. - */ -#define PAL_PORT(line) \ - ((stm32_gpio_t *)(((uint32_t)(line)) & 0xFFFFFFF0U)) - -/** - * @brief Decodes a pad identifier from a line identifier. - */ -#define PAL_PAD(line) \ - ((uint32_t)((uint32_t)(line) & 0x0000000FU)) - -/** - * @brief Value identifying an invalid line. - */ -#define PAL_NOLINE 0U -/** @} */ - -/** - * @brief STM32 GPIO registers block. - */ -typedef struct { - - volatile uint32_t MODER; - volatile uint32_t OTYPER; - volatile uint32_t OSPEEDR; - volatile uint32_t PUPDR; - volatile uint32_t IDR; - volatile uint32_t ODR; - volatile union { - uint32_t W; - struct { - uint16_t set; - uint16_t clear; - } H; - } BSRR; - volatile uint32_t LOCKR; - volatile uint32_t AFRL; - volatile uint32_t AFRH; - volatile uint32_t BRR; - volatile uint32_t ASCR; -} stm32_gpio_t; - -/** - * @brief GPIO port setup info. - */ -typedef struct { - /** Initial value for MODER register.*/ - uint32_t moder; - /** Initial value for OTYPER register.*/ - uint32_t otyper; - /** Initial value for OSPEEDR register.*/ - uint32_t ospeedr; - /** Initial value for PUPDR register.*/ - uint32_t pupdr; - /** Initial value for ODR register.*/ - uint32_t odr; - /** Initial value for AFRL register.*/ - uint32_t afrl; - /** Initial value for AFRH register.*/ - uint32_t afrh; - /** Initial value for ASCR register.*/ - uint32_t ascr; - /** Initial value for LOCKR register.*/ - uint32_t lockr; -} stm32_gpio_setup_t; - -/** - * @brief STM32 GPIO static initializer. - * @details An instance of this structure must be passed to @p palInit() at - * system startup time in order to initialize the digital I/O - * subsystem. This represents only the initial setup, specific pads - * or whole ports can be reprogrammed at later time. - */ -typedef struct { -#if STM32_HAS_GPIOA || defined(__DOXYGEN__) - /** @brief Port A setup data.*/ - stm32_gpio_setup_t PAData; -#endif -#if STM32_HAS_GPIOB || defined(__DOXYGEN__) - /** @brief Port B setup data.*/ - stm32_gpio_setup_t PBData; -#endif -#if STM32_HAS_GPIOC || defined(__DOXYGEN__) - /** @brief Port C setup data.*/ - stm32_gpio_setup_t PCData; -#endif -#if STM32_HAS_GPIOD || defined(__DOXYGEN__) - /** @brief Port D setup data.*/ - stm32_gpio_setup_t PDData; -#endif -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) - /** @brief Port E setup data.*/ - stm32_gpio_setup_t PEData; -#endif -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) - /** @brief Port F setup data.*/ - stm32_gpio_setup_t PFData; -#endif -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) - /** @brief Port G setup data.*/ - stm32_gpio_setup_t PGData; -#endif -#if STM32_HAS_GPIOH || defined(__DOXYGEN__) - /** @brief Port H setup data.*/ - stm32_gpio_setup_t PHData; -#endif -#if STM32_HAS_GPIOI || defined(__DOXYGEN__) - /** @brief Port I setup data.*/ - stm32_gpio_setup_t PIData; -#endif -#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) - /** @brief Port I setup data.*/ - stm32_gpio_setup_t PJData; -#endif -#if STM32_HAS_GPIOK || defined(__DOXYGEN__) - /** @brief Port I setup data.*/ - stm32_gpio_setup_t PKData; -#endif -} PALConfig; - -/** - * @brief Type of digital I/O port sized unsigned integer. - */ -typedef uint32_t ioportmask_t; - -/** - * @brief Type of digital I/O modes. - */ -typedef uint32_t iomode_t; - -/** - * @brief Type of an I/O line. - */ -typedef uint32_t ioline_t; - -/** - * @brief Port Identifier. - * @details This type can be a scalar or some kind of pointer, do not make - * any assumption about it, use the provided macros when populating - * variables of this type. - */ -typedef stm32_gpio_t * ioportid_t; - -/*===========================================================================*/ -/* I/O Ports Identifiers. */ -/* The low level driver wraps the definitions already present in the STM32 */ -/* firmware library. */ -/*===========================================================================*/ - -/** - * @brief GPIO port A identifier. - */ -#if STM32_HAS_GPIOA || defined(__DOXYGEN__) -#define IOPORT1 GPIOA -#endif - -/** - * @brief GPIO port B identifier. - */ -#if STM32_HAS_GPIOB || defined(__DOXYGEN__) -#define IOPORT2 GPIOB -#endif - -/** - * @brief GPIO port C identifier. - */ -#if STM32_HAS_GPIOC || defined(__DOXYGEN__) -#define IOPORT3 GPIOC -#endif - -/** - * @brief GPIO port D identifier. - */ -#if STM32_HAS_GPIOD || defined(__DOXYGEN__) -#define IOPORT4 GPIOD -#endif - -/** - * @brief GPIO port E identifier. - */ -#if STM32_HAS_GPIOE || defined(__DOXYGEN__) -#define IOPORT5 GPIOE -#endif - -/** - * @brief GPIO port F identifier. - */ -#if STM32_HAS_GPIOF || defined(__DOXYGEN__) -#define IOPORT6 GPIOF -#endif - -/** - * @brief GPIO port G identifier. - */ -#if STM32_HAS_GPIOG || defined(__DOXYGEN__) -#define IOPORT7 GPIOG -#endif - -/** - * @brief GPIO port H identifier. - */ -#if STM32_HAS_GPIOH || defined(__DOXYGEN__) -#define IOPORT8 GPIOH -#endif - -/** - * @brief GPIO port I identifier. - */ -#if STM32_HAS_GPIOI || defined(__DOXYGEN__) -#define IOPORT9 GPIOI -#endif - -/** - * @brief GPIO port J identifier. - */ -#if STM32_HAS_GPIOJ || defined(__DOXYGEN__) -#define IOPORT10 GPIOJ -#endif - -/** - * @brief GPIO port K identifier. - */ -#if STM32_HAS_GPIOK || defined(__DOXYGEN__) -#define IOPORT11 GPIOK -#endif - -/*===========================================================================*/ -/* Implementation, some of the following macros could be implemented as */ -/* functions, if so please put them in pal_lld.c. */ -/*===========================================================================*/ - -/** - * @brief GPIO ports subsystem initialization. - * - * @notapi - */ -#define pal_lld_init(config) _pal_lld_init(config) - -/** - * @brief Reads an I/O port. - * @details This function is implemented by reading the GPIO IDR register, the - * implementation has no side effects. - * @note This function is not meant to be invoked directly by the application - * code. - * - * @param[in] port port identifier - * @return The port bits. - * - * @notapi - */ -#define pal_lld_readport(port) ((port)->IDR) - -/** - * @brief Reads the output latch. - * @details This function is implemented by reading the GPIO ODR register, the - * implementation has no side effects. - * @note This function is not meant to be invoked directly by the application - * code. - * - * @param[in] port port identifier - * @return The latched logical states. - * - * @notapi - */ -#define pal_lld_readlatch(port) ((port)->ODR) - -/** - * @brief Writes on a I/O port. - * @details This function is implemented by writing the GPIO ODR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] bits bits to be written on the specified port - * - * @notapi - */ -#define pal_lld_writeport(port, bits) ((port)->ODR = (bits)) - -/** - * @brief Sets a bits mask on a I/O port. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] bits bits to be ORed on the specified port - * - * @notapi - */ -#define pal_lld_setport(port, bits) ((port)->BSRR.H.set = (uint16_t)(bits)) - -/** - * @brief Clears a bits mask on a I/O port. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] bits bits to be cleared on the specified port - * - * @notapi - */ -#define pal_lld_clearport(port, bits) ((port)->BSRR.H.clear = (uint16_t)(bits)) - -/** - * @brief Writes a group of bits. - * @details This function is implemented by writing the GPIO BSRR register, the - * implementation has no side effects. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset the group bit offset within the port - * @param[in] bits bits to be written. Values exceeding the group - * width are masked. - * - * @notapi - */ -#define pal_lld_writegroup(port, mask, offset, bits) \ - ((port)->BSRR.W = ((~(bits) & (mask)) << (16U + (offset))) | \ - (((bits) & (mask)) << (offset))) - -/** - * @brief Pads group mode setup. - * @details This function programs a pads group belonging to the same port - * with the specified mode. - * - * @param[in] port port identifier - * @param[in] mask group mask - * @param[in] offset group bit offset within the port - * @param[in] mode group mode - * - * @notapi - */ -#define pal_lld_setgroupmode(port, mask, offset, mode) \ - _pal_lld_setgroupmode(port, mask << offset, mode) - -/** - * @brief Writes a logical state on an output pad. - * - * @param[in] port port identifier - * @param[in] pad pad number within the port - * @param[in] bit logical value, the value must be @p PAL_LOW or - * @p PAL_HIGH - * - * @notapi - */ -#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit) - -extern const PALConfig pal_default_config; - -#ifdef __cplusplus -extern "C" { -#endif - void _pal_lld_init(const PALConfig *config); - void _pal_lld_setgroupmode(ioportid_t port, - ioportmask_t mask, - iomode_t mode); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_PAL */ - -#endif /* _PAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c new file mode 100644 index 000000000..38243c8a0 --- /dev/null +++ b/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.c @@ -0,0 +1,855 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/I2Cv1/i2c_lld.c + * @brief STM32 I2C subsystem low level driver source. + * + * @addtogroup I2C + * @{ + */ + +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define I2C1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_CHN) + +#define I2C1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_CHN) + +#define I2C2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_CHN) + +#define I2C2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_CHN) + +#define I2C3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_CHN) + +#define I2C3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_CHN) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define I2C_EV5_MASTER_MODE_SELECT \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_SB)) + +#define I2C_EV6_MASTER_TRA_MODE_SELECTED \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \ + I2C_SR1_ADDR | I2C_SR1_TXE)) + +#define I2C_EV6_MASTER_REC_MODE_SELECTED \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY)<< 16) | I2C_SR1_ADDR)) + +#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \ + I2C_SR1_BTF | I2C_SR1_TXE)) + +#define I2C_EV9_MASTER_ADD10 \ + ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_ADD10)) + +#define I2C_EV_MASK 0x00FFFFFF + +#define I2C_ERROR_MASK \ + ((uint16_t)(I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_OVR | \ + I2C_SR1_PECERR | I2C_SR1_TIMEOUT | I2C_SR1_SMBALERT)) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2C1 driver identifier.*/ +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +I2CDriver I2CD1; +#endif + +/** @brief I2C2 driver identifier.*/ +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +I2CDriver I2CD2; +#endif + +/** @brief I2C3 driver identifier.*/ +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +I2CDriver I2CD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Aborts an I2C transaction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_abort_operation(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + + /* Stops the I2C peripheral.*/ + dp->CR1 = I2C_CR1_SWRST; + dp->CR1 = 0; + dp->CR2 = 0; + dp->SR1 = 0; + + /* Stops the associated DMA streams.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); +} + +/** + * @brief Set clock speed. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_set_clock(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint16_t regCCR, clock_div; + int32_t clock_speed = i2cp->config->clock_speed; + i2cdutycycle_t duty = i2cp->config->duty_cycle; + + osalDbgCheck((i2cp != NULL) && + (clock_speed > 0) && + (clock_speed <= 4000000)); + + /* CR2 Configuration.*/ + dp->CR2 &= (uint16_t)~I2C_CR2_FREQ; + dp->CR2 |= (uint16_t)I2C_CLK_FREQ; + + /* CCR Configuration.*/ + regCCR = 0; + clock_div = I2C_CCR_CCR; + + if (clock_speed <= 100000) { + /* Configure clock_div in standard mode.*/ + osalDbgAssert(duty == STD_DUTY_CYCLE, "invalid standard mode duty cycle"); + + /* Standard mode clock_div calculate: Tlow/Thigh = 1/1.*/ + osalDbgAssert((STM32_PCLK1 % (clock_speed * 2)) == 0, + "PCLK1 must be divisible without remainder"); + clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 2)); + + osalDbgAssert(clock_div >= 0x04, + "clock divider less then 0x04 not allowed"); + regCCR |= (clock_div & I2C_CCR_CCR); + + /* Sets the Maximum Rise Time for standard mode.*/ + dp->TRISE = I2C_CLK_FREQ + 1; + } + else if (clock_speed <= 400000) { + /* Configure clock_div in fast mode.*/ + osalDbgAssert((duty == FAST_DUTY_CYCLE_2) || + (duty == FAST_DUTY_CYCLE_16_9), + "invalid fast mode duty cycle"); + + if (duty == FAST_DUTY_CYCLE_2) { + /* Fast mode clock_div calculate: Tlow/Thigh = 2/1.*/ + osalDbgAssert((STM32_PCLK1 % (clock_speed * 3)) == 0, + "PCLK1 must be divided without remainder"); + clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3)); + } + else if (duty == FAST_DUTY_CYCLE_16_9) { + /* Fast mode clock_div calculate: Tlow/Thigh = 16/9.*/ + osalDbgAssert((STM32_PCLK1 % (clock_speed * 25)) == 0, + "PCLK1 must be divided without remainder"); + clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25)); + regCCR |= I2C_CCR_DUTY; + } + + osalDbgAssert(clock_div >= 0x01, + "clock divider less then 0x04 not allowed"); + regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR)); + + /* Sets the Maximum Rise Time for fast mode.*/ + dp->TRISE = (I2C_CLK_FREQ * 300 / 1000) + 1; + } + + osalDbgAssert((clock_div <= I2C_CCR_CCR), "the selected clock is too low"); + + dp->CCR = regCCR; +} + +/** + * @brief Set operation mode of I2C hardware. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_set_opmode(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + i2copmode_t opmode = i2cp->config->op_mode; + uint16_t regCR1; + + regCR1 = dp->CR1; + switch (opmode) { + case OPMODE_I2C: + regCR1 &= (uint16_t)~(I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); + break; + case OPMODE_SMBUS_DEVICE: + regCR1 |= I2C_CR1_SMBUS; + regCR1 &= (uint16_t)~(I2C_CR1_SMBTYPE); + break; + case OPMODE_SMBUS_HOST: + regCR1 |= (I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); + break; + } + dp->CR1 = regCR1; +} + +/** + * @brief I2C shared ISR code. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t regSR2 = dp->SR2; + uint32_t event = dp->SR1; + + /* Interrupts are disabled just before dmaStreamEnable() because there + is no need of interrupts until next transaction begin. All the work is + done by the DMA.*/ + switch (I2C_EV_MASK & (event | (regSR2 << 16))) { + case I2C_EV5_MASTER_MODE_SELECT: + if ((i2cp->addr >> 8) > 0) { + /* 10-bit address: 1 1 1 1 0 X X R/W */ + dp->DR = 0xF0 | (0x6 & (i2cp->addr >> 8)) | (0x1 & i2cp->addr); + } else { + dp->DR = i2cp->addr; + } + break; + case I2C_EV9_MASTER_ADD10: + /* Set second addr byte (10-bit addressing)*/ + dp->DR = (0xFF & (i2cp->addr >> 1)); + break; + case I2C_EV6_MASTER_REC_MODE_SELECTED: + dp->CR2 &= ~I2C_CR2_ITEVTEN; + dmaStreamEnable(i2cp->dmarx); + dp->CR2 |= I2C_CR2_LAST; /* Needed in receiver mode. */ + if (dmaStreamGetTransactionSize(i2cp->dmarx) < 2) + dp->CR1 &= ~I2C_CR1_ACK; + break; + case I2C_EV6_MASTER_TRA_MODE_SELECTED: + dp->CR2 &= ~I2C_CR2_ITEVTEN; + dmaStreamEnable(i2cp->dmatx); + break; + case I2C_EV8_2_MASTER_BYTE_TRANSMITTED: + /* Catches BTF event after the end of transmission.*/ + if (dmaStreamGetTransactionSize(i2cp->dmarx) > 0) { + /* Starts "read after write" operation, LSB = 1 -> receive.*/ + i2cp->addr |= 0x01; + dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK; + return; + } + dp->CR2 &= ~I2C_CR2_ITEVTEN; + dp->CR1 |= I2C_CR1_STOP; + _i2c_wakeup_isr(i2cp); + break; + default: + break; + } + /* Clear ADDR flag. */ + if (event & (I2C_SR1_ADDR | I2C_SR1_ADD10)) + (void)dp->SR2; +} + +/** + * @brief DMA RX end IRQ handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] flags pre-shifted content of the ISR register + * + * @notapi + */ +static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) { + I2C_TypeDef *dp = i2cp->i2c; + + /* DMA errors handling.*/ +#if defined(STM32_I2C_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2C_DMA_ERROR_HOOK(i2cp); + } +#else + (void)flags; +#endif + + dmaStreamDisable(i2cp->dmarx); + + dp->CR2 &= ~I2C_CR2_LAST; + dp->CR1 &= ~I2C_CR1_ACK; + dp->CR1 |= I2C_CR1_STOP; + _i2c_wakeup_isr(i2cp); +} + +/** + * @brief DMA TX end IRQ handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { + I2C_TypeDef *dp = i2cp->i2c; + + /* DMA errors handling.*/ +#if defined(STM32_I2C_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2C_DMA_ERROR_HOOK(i2cp); + } +#else + (void)flags; +#endif + + dmaStreamDisable(i2cp->dmatx); + /* Enables interrupts to catch BTF event meaning transmission part complete. + Interrupt handler will decide to generate STOP or to begin receiving part + of R/W transaction itself.*/ + dp->CR2 |= I2C_CR2_ITEVTEN; +} + +/** + * @brief I2C error handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] sr content of the SR1 register to be decoded + * + * @notapi + */ +static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr) { + + /* Clears interrupt flags just to be safe.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); + + i2cp->errors = I2C_NO_ERROR; + + if (sr & I2C_SR1_BERR) /* Bus error. */ + i2cp->errors |= I2C_BUS_ERROR; + + if (sr & I2C_SR1_ARLO) /* Arbitration lost. */ + i2cp->errors |= I2C_ARBITRATION_LOST; + + if (sr & I2C_SR1_AF) { /* Acknowledge fail. */ + i2cp->i2c->CR2 &= ~I2C_CR2_ITEVTEN; + i2cp->i2c->CR1 |= I2C_CR1_STOP; /* Setting stop bit. */ + i2cp->errors |= I2C_ACK_FAILURE; + } + + if (sr & I2C_SR1_OVR) /* Overrun. */ + i2cp->errors |= I2C_OVERRUN; + + if (sr & I2C_SR1_TIMEOUT) /* SMBus Timeout. */ + i2cp->errors |= I2C_TIMEOUT; + + if (sr & I2C_SR1_PECERR) /* PEC error. */ + i2cp->errors |= I2C_PEC_ERROR; + + if (sr & I2C_SR1_SMBALERT) /* SMBus alert. */ + i2cp->errors |= I2C_SMB_ALERT; + + /* If some error has been identified then sends wakes the waiting thread.*/ + if (i2cp->errors != I2C_NO_ERROR) + _i2c_wakeup_error_isr(i2cp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +/** + * @brief I2C1 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + i2c_lld_serve_event_interrupt(&I2CD1); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief I2C1 error interrupt handler. + */ +OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { + uint16_t sr = I2CD1.i2c->SR1; + + OSAL_IRQ_PROLOGUE(); + + I2CD1.i2c->SR1 = ~(sr & I2C_ERROR_MASK); + i2c_lld_serve_error_interrupt(&I2CD1, sr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +/** + * @brief I2C2 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + i2c_lld_serve_event_interrupt(&I2CD2); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief I2C2 error interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { + uint16_t sr = I2CD2.i2c->SR1; + + OSAL_IRQ_PROLOGUE(); + + I2CD2.i2c->SR1 = ~(sr & I2C_ERROR_MASK); + i2c_lld_serve_error_interrupt(&I2CD2, sr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +/** + * @brief I2C3 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + i2c_lld_serve_event_interrupt(&I2CD3); + + OSAL_IRQ_EPILOGUE(); +} + +/** + * @brief I2C3 error interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { + uint16_t sr = I2CD3.i2c->SR1; + + OSAL_IRQ_PROLOGUE(); + + I2CD3.i2c->SR1 = ~(sr & I2C_ERROR_MASK); + i2c_lld_serve_error_interrupt(&I2CD3, sr); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_I2C_USE_I2C3 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + +#if STM32_I2C_USE_I2C1 + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = I2C1; + I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); + I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = I2C2; + I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); + I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + i2cObjectInit(&I2CD3); + I2CD3.thread = NULL; + I2CD3.i2c = I2C3; + I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); + I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); +#endif /* STM32_I2C_USE_I2C3 */ +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + + i2cp->txdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | + STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DIR_M2P; + i2cp->rxdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | + STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | + STM32_DMA_CR_DIR_P2M; + + /* If in stopped state then enables the I2C and DMA clocks.*/ + if (i2cp->state == I2C_STOP) { + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { + bool b; + + rccResetI2C1(); + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableI2C1(FALSE); + nvicEnableVector(I2C1_EV_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); + nvicEnableVector(I2C1_ER_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { + bool b; + + rccResetI2C2(); + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableI2C2(FALSE); + nvicEnableVector(I2C2_EV_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); + nvicEnableVector(I2C2_ER_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { + bool b; + + rccResetI2C3(); + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableI2C3(FALSE); + nvicEnableVector(I2C3_EV_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); + nvicEnableVector(I2C3_ER_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_I2C3 */ + } + + /* I2C registers pointed by the DMA.*/ + dmaStreamSetPeripheral(i2cp->dmarx, &dp->DR); + dmaStreamSetPeripheral(i2cp->dmatx, &dp->DR); + + /* Reset i2c peripheral.*/ + dp->CR1 = I2C_CR1_SWRST; + dp->CR1 = 0; + dp->CR2 = I2C_CR2_ITERREN | I2C_CR2_DMAEN; + + /* Setup I2C parameters.*/ + i2c_lld_set_clock(i2cp); + i2c_lld_set_opmode(i2cp); + + /* Ready to go.*/ + dp->CR1 |= I2C_CR1_PE; +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + /* If not in stopped state then disables the I2C clock.*/ + if (i2cp->state != I2C_STOP) { + + /* I2C disable.*/ + i2c_lld_abort_operation(i2cp); + dmaStreamRelease(i2cp->dmatx); + dmaStreamRelease(i2cp->dmarx); + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { + nvicDisableVector(I2C1_EV_IRQn); + nvicDisableVector(I2C1_ER_IRQn); + rccDisableI2C1(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { + nvicDisableVector(I2C2_EV_IRQn); + nvicDisableVector(I2C2_ER_IRQn); + rccDisableI2C2(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { + nvicDisableVector(I2C3_EV_IRQn); + nvicDisableVector(I2C3_ER_IRQn); + rccDisableI2C3(FALSE); + } +#endif + } +} + +/** + * @brief Receives data via the I2C bus as master. + * @details Number of receiving bytes must be more than 1 on STM32F1x. This is + * hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + I2C_TypeDef *dp = i2cp->i2c; + systime_t start, end; + +#if defined(STM32F1XX_I2C) + osalDbgCheck(rxbytes > 1); +#endif + + /* Resetting error flags for this transfer.*/ + i2cp->errors = I2C_NO_ERROR; + + /* Initializes driver fields, LSB = 1 -> receive.*/ + i2cp->addr = (addr << 1) | 0x01; + + /* Releases the lock from high level driver.*/ + osalSysUnlock(); + + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); + + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); + end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); + + /* Waits until BUSY flag is reset or, alternatively, for a timeout + condition.*/ + while (true) { + osalSysLock(); + + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ + if (!(dp->SR2 & I2C_SR2_BUSY) && !(dp->CR1 & I2C_CR1_STOP)) + break; + + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) + return MSG_TIMEOUT; + + osalSysUnlock(); + } + + /* Starts the operation.*/ + dp->CR2 |= I2C_CR2_ITEVTEN; + dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK; + + /* Waits for the operation completion or a timeout.*/ + return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); +} + +/** + * @brief Transmits data via the I2C bus as master. + * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. + * This is hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[in] txbuf pointer to the transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + I2C_TypeDef *dp = i2cp->i2c; + systime_t start, end; + +#if defined(STM32F1XX_I2C) + osalDbgCheck((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))); +#endif + + /* Resetting error flags for this transfer.*/ + i2cp->errors = I2C_NO_ERROR; + + /* Initializes driver fields, LSB = 0 -> transmit.*/ + i2cp->addr = (addr << 1); + + /* Releases the lock from high level driver.*/ + osalSysUnlock(); + + /* TX DMA setup.*/ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, txbuf); + dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); + + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); + + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); + end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); + + /* Waits until BUSY flag is reset or, alternatively, for a timeout + condition.*/ + while (true) { + osalSysLock(); + + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ + if (!(dp->SR2 & I2C_SR2_BUSY) && !(dp->CR1 & I2C_CR1_STOP)) + break; + + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) + return MSG_TIMEOUT; + + osalSysUnlock(); + } + + /* Starts the operation.*/ + dp->CR2 |= I2C_CR2_ITEVTEN; + dp->CR1 |= I2C_CR1_START; + + /* Waits for the operation completion or a timeout.*/ + return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); +} + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.h new file mode 100644 index 000000000..5e3024b00 --- /dev/null +++ b/os/hal/ports/STM32/LLD/I2Cv1/hal_i2c_lld.h @@ -0,0 +1,513 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/I2Cv1/i2c_lld.h + * @brief STM32 I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#ifndef _I2C_LLD_H_ +#define _I2C_LLD_H_ + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Peripheral clock frequency. + */ +#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000) + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2C1 driver enable switch. + * @details If set to @p TRUE the support for I2C1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C1 FALSE +#endif + +/** + * @brief I2C2 driver enable switch. + * @details If set to @p TRUE the support for I2C2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C2 FALSE +#endif + +/** + * @brief I2C3 driver enable switch. + * @details If set to @p TRUE the support for I2C3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C3 FALSE +#endif + +/** + * @brief I2C timeout on busy condition in milliseconds. + */ +#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_I2C_BUSY_TIMEOUT 50 +#endif + +/** + * @brief I2C1 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C2 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C3 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_IRQ_PRIORITY 10 +#endif + +/** +* @brief I2C1 DMA priority (0..3|lowest..highest). +* @note The priority level is used for both the TX and RX DMA streams but +* because of the streams ordering the RX stream has always priority +* over the TX stream. +*/ +#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#endif + +/** +* @brief I2C2 DMA priority (0..3|lowest..highest). +* @note The priority level is used for both the TX and RX DMA streams but +* because of the streams ordering the RX stream has always priority +* over the TX stream. +*/ +#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#endif + +/** +* @brief I2C3 DMA priority (0..3|lowest..highest). +* @note The priority level is used for both the TX and RX DMA streams but +* because of the streams ordering the RX stream has always priority +* over the TX stream. +*/ +#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C DMA error hook. + * @note The default action for DMA errors is a system halt because DMA + * error can only happen because programming errors. + */ +#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") +#endif + +#if STM32_ADVANCED_DMA || defined(__DOXYGEN__) + +/** + * @brief DMA stream used for I2C1 RX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C1_RX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0) +#endif + +/** + * @brief DMA stream used for I2C1 TX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C1_TX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) +#endif + +/** + * @brief DMA stream used for I2C2 RX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C2_RX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) +#endif + +/** + * @brief DMA stream used for I2C2 TX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C2_TX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) +#endif + +/** + * @brief DMA stream used for I2C3 RX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C3_RX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) +#endif + +/** + * @brief DMA stream used for I2C3 TX operations. + * @note This option is only available on platforms with enhanced DMA. + */ +#if !defined(STM32_I2C_I2C3_TX_DMA_STREAM) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) +#endif + +#else /* !STM32_ADVANCED_DMA */ + +/* Fixed streams for platforms using the old DMA peripheral, the values are + valid for both STM32F1xx and STM32L1xx.*/ +#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) +#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) +#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) +#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) + +#endif /* !STM32_ADVANCED_DMA*/ + +/* Flag for the whole STM32F1XX family. */ +#if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ + defined(STM32F10X_HD_VL) || defined(STM32F10X_LD) || \ + defined(STM32F10X_MD) || defined(STM32F10X_HD) || \ + defined(STM32F10X_XL) || defined(STM32F10X_CL) +#define STM32F1XX_I2C +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/** @brief error checks */ +#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 +#error "I2C1 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 +#error "I2C2 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 +#error "I2C3 not present in the selected device" +#endif + +#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && \ + !STM32_I2C_USE_I2C3 +#error "I2C driver activated but no I2C peripheral assigned" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C3" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C3" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) +#error "I2C1 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) +#error "I2C2 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 RX" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 TX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 RX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 TX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 RX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/* Check clock range. */ +#if defined(STM32F4XX) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32L1XX) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 32) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32F2XX) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 30) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ + defined(STM32F10X_HD_VL) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 24) +#error "I2C peripheral clock frequency out of range." +#endif + +#elif defined(STM32F10X_LD) || defined(STM32F10X_MD) || \ + defined(STM32F10X_HD) || defined(STM32F10X_XL) || \ + defined(STM32F10X_CL) +#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 36) +#error "I2C peripheral clock frequency out of range." +#endif +#else +#error "unspecified, unsupported or invalid STM32 platform" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type representing an I2C address. + */ +typedef uint16_t i2caddr_t; + +/** + * @brief Type of I2C driver condition flags. + */ +typedef uint32_t i2cflags_t; + +/** + * @brief Supported modes for the I2C bus. + */ +typedef enum { + OPMODE_I2C = 1, + OPMODE_SMBUS_DEVICE = 2, + OPMODE_SMBUS_HOST = 3, +} i2copmode_t; + +/** + * @brief Supported duty cycle modes for the I2C bus. + */ +typedef enum { + STD_DUTY_CYCLE = 1, + FAST_DUTY_CYCLE_2 = 2, + FAST_DUTY_CYCLE_16_9 = 3, +} i2cdutycycle_t; + +/** + * @brief Type of I2C driver configuration structure. + */ +typedef struct { + /* End of the mandatory fields.*/ + i2copmode_t op_mode; /**< @brief Specifies the I2C mode. */ + uint32_t clock_speed; /**< @brief Specifies the clock frequency. + @note Must be set to a value lower + than 400kHz. */ + i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode + duty cycle. */ +} I2CConfig; + +/** + * @brief Type of a structure representing an I2C driver. + */ +typedef struct I2CDriver I2CDriver; + +/** + * @brief Structure representing an I2C driver. + */ +struct I2CDriver { + /** + * @brief Driver state. + */ + i2cstate_t state; + /** + * @brief Current configuration data. + */ + const I2CConfig *config; + /** + * @brief Error flags. + */ + i2cflags_t errors; +#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion. + */ + thread_reference_t thread; + /** + * @brief Current slave address without R/W bit. + */ + i2caddr_t addr; + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; + /** + * @brief Receive DMA channel. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief Pointer to the I2Cx registers block. + */ + I2C_TypeDef *i2c; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Get errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +#if STM32_I2C_USE_I2C1 +extern I2CDriver I2CD1; +#endif + +#if STM32_I2C_USE_I2C2 +extern I2CDriver I2CD2; +#endif + +#if STM32_I2C_USE_I2C3 +extern I2CDriver I2CD3; +#endif +#endif /* !defined(__DOXYGEN__) */ + +#ifdef __cplusplus +extern "C" { +#endif + void i2c_lld_init(void); + void i2c_lld_start(I2CDriver *i2cp); + void i2c_lld_stop(I2CDriver *i2cp); + msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C */ + +#endif /* _I2C_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.c deleted file mode 100644 index 38243c8a0..000000000 --- a/os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.c +++ /dev/null @@ -1,855 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/I2Cv1/i2c_lld.c - * @brief STM32 I2C subsystem low level driver source. - * - * @addtogroup I2C - * @{ - */ - -#include "hal.h" - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define I2C1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_CHN) - -#define I2C1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_CHN) - -#define I2C2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_CHN) - -#define I2C2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_CHN) - -#define I2C3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_CHN) - -#define I2C3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_CHN) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -#define I2C_EV5_MASTER_MODE_SELECT \ - ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_SB)) - -#define I2C_EV6_MASTER_TRA_MODE_SELECTED \ - ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \ - I2C_SR1_ADDR | I2C_SR1_TXE)) - -#define I2C_EV6_MASTER_REC_MODE_SELECTED \ - ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY)<< 16) | I2C_SR1_ADDR)) - -#define I2C_EV8_2_MASTER_BYTE_TRANSMITTED \ - ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY | I2C_SR2_TRA) << 16) | \ - I2C_SR1_BTF | I2C_SR1_TXE)) - -#define I2C_EV9_MASTER_ADD10 \ - ((uint32_t)(((I2C_SR2_MSL | I2C_SR2_BUSY) << 16) | I2C_SR1_ADD10)) - -#define I2C_EV_MASK 0x00FFFFFF - -#define I2C_ERROR_MASK \ - ((uint16_t)(I2C_SR1_BERR | I2C_SR1_ARLO | I2C_SR1_AF | I2C_SR1_OVR | \ - I2C_SR1_PECERR | I2C_SR1_TIMEOUT | I2C_SR1_SMBALERT)) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief I2C1 driver identifier.*/ -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -I2CDriver I2CD1; -#endif - -/** @brief I2C2 driver identifier.*/ -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -I2CDriver I2CD2; -#endif - -/** @brief I2C3 driver identifier.*/ -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -I2CDriver I2CD3; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Aborts an I2C transaction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_abort_operation(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - - /* Stops the I2C peripheral.*/ - dp->CR1 = I2C_CR1_SWRST; - dp->CR1 = 0; - dp->CR2 = 0; - dp->SR1 = 0; - - /* Stops the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); -} - -/** - * @brief Set clock speed. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_set_clock(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint16_t regCCR, clock_div; - int32_t clock_speed = i2cp->config->clock_speed; - i2cdutycycle_t duty = i2cp->config->duty_cycle; - - osalDbgCheck((i2cp != NULL) && - (clock_speed > 0) && - (clock_speed <= 4000000)); - - /* CR2 Configuration.*/ - dp->CR2 &= (uint16_t)~I2C_CR2_FREQ; - dp->CR2 |= (uint16_t)I2C_CLK_FREQ; - - /* CCR Configuration.*/ - regCCR = 0; - clock_div = I2C_CCR_CCR; - - if (clock_speed <= 100000) { - /* Configure clock_div in standard mode.*/ - osalDbgAssert(duty == STD_DUTY_CYCLE, "invalid standard mode duty cycle"); - - /* Standard mode clock_div calculate: Tlow/Thigh = 1/1.*/ - osalDbgAssert((STM32_PCLK1 % (clock_speed * 2)) == 0, - "PCLK1 must be divisible without remainder"); - clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 2)); - - osalDbgAssert(clock_div >= 0x04, - "clock divider less then 0x04 not allowed"); - regCCR |= (clock_div & I2C_CCR_CCR); - - /* Sets the Maximum Rise Time for standard mode.*/ - dp->TRISE = I2C_CLK_FREQ + 1; - } - else if (clock_speed <= 400000) { - /* Configure clock_div in fast mode.*/ - osalDbgAssert((duty == FAST_DUTY_CYCLE_2) || - (duty == FAST_DUTY_CYCLE_16_9), - "invalid fast mode duty cycle"); - - if (duty == FAST_DUTY_CYCLE_2) { - /* Fast mode clock_div calculate: Tlow/Thigh = 2/1.*/ - osalDbgAssert((STM32_PCLK1 % (clock_speed * 3)) == 0, - "PCLK1 must be divided without remainder"); - clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 3)); - } - else if (duty == FAST_DUTY_CYCLE_16_9) { - /* Fast mode clock_div calculate: Tlow/Thigh = 16/9.*/ - osalDbgAssert((STM32_PCLK1 % (clock_speed * 25)) == 0, - "PCLK1 must be divided without remainder"); - clock_div = (uint16_t)(STM32_PCLK1 / (clock_speed * 25)); - regCCR |= I2C_CCR_DUTY; - } - - osalDbgAssert(clock_div >= 0x01, - "clock divider less then 0x04 not allowed"); - regCCR |= (I2C_CCR_FS | (clock_div & I2C_CCR_CCR)); - - /* Sets the Maximum Rise Time for fast mode.*/ - dp->TRISE = (I2C_CLK_FREQ * 300 / 1000) + 1; - } - - osalDbgAssert((clock_div <= I2C_CCR_CCR), "the selected clock is too low"); - - dp->CCR = regCCR; -} - -/** - * @brief Set operation mode of I2C hardware. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_set_opmode(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - i2copmode_t opmode = i2cp->config->op_mode; - uint16_t regCR1; - - regCR1 = dp->CR1; - switch (opmode) { - case OPMODE_I2C: - regCR1 &= (uint16_t)~(I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); - break; - case OPMODE_SMBUS_DEVICE: - regCR1 |= I2C_CR1_SMBUS; - regCR1 &= (uint16_t)~(I2C_CR1_SMBTYPE); - break; - case OPMODE_SMBUS_HOST: - regCR1 |= (I2C_CR1_SMBUS|I2C_CR1_SMBTYPE); - break; - } - dp->CR1 = regCR1; -} - -/** - * @brief I2C shared ISR code. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint32_t regSR2 = dp->SR2; - uint32_t event = dp->SR1; - - /* Interrupts are disabled just before dmaStreamEnable() because there - is no need of interrupts until next transaction begin. All the work is - done by the DMA.*/ - switch (I2C_EV_MASK & (event | (regSR2 << 16))) { - case I2C_EV5_MASTER_MODE_SELECT: - if ((i2cp->addr >> 8) > 0) { - /* 10-bit address: 1 1 1 1 0 X X R/W */ - dp->DR = 0xF0 | (0x6 & (i2cp->addr >> 8)) | (0x1 & i2cp->addr); - } else { - dp->DR = i2cp->addr; - } - break; - case I2C_EV9_MASTER_ADD10: - /* Set second addr byte (10-bit addressing)*/ - dp->DR = (0xFF & (i2cp->addr >> 1)); - break; - case I2C_EV6_MASTER_REC_MODE_SELECTED: - dp->CR2 &= ~I2C_CR2_ITEVTEN; - dmaStreamEnable(i2cp->dmarx); - dp->CR2 |= I2C_CR2_LAST; /* Needed in receiver mode. */ - if (dmaStreamGetTransactionSize(i2cp->dmarx) < 2) - dp->CR1 &= ~I2C_CR1_ACK; - break; - case I2C_EV6_MASTER_TRA_MODE_SELECTED: - dp->CR2 &= ~I2C_CR2_ITEVTEN; - dmaStreamEnable(i2cp->dmatx); - break; - case I2C_EV8_2_MASTER_BYTE_TRANSMITTED: - /* Catches BTF event after the end of transmission.*/ - if (dmaStreamGetTransactionSize(i2cp->dmarx) > 0) { - /* Starts "read after write" operation, LSB = 1 -> receive.*/ - i2cp->addr |= 0x01; - dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK; - return; - } - dp->CR2 &= ~I2C_CR2_ITEVTEN; - dp->CR1 |= I2C_CR1_STOP; - _i2c_wakeup_isr(i2cp); - break; - default: - break; - } - /* Clear ADDR flag. */ - if (event & (I2C_SR1_ADDR | I2C_SR1_ADD10)) - (void)dp->SR2; -} - -/** - * @brief DMA RX end IRQ handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] flags pre-shifted content of the ISR register - * - * @notapi - */ -static void i2c_lld_serve_rx_end_irq(I2CDriver *i2cp, uint32_t flags) { - I2C_TypeDef *dp = i2cp->i2c; - - /* DMA errors handling.*/ -#if defined(STM32_I2C_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2C_DMA_ERROR_HOOK(i2cp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(i2cp->dmarx); - - dp->CR2 &= ~I2C_CR2_LAST; - dp->CR1 &= ~I2C_CR1_ACK; - dp->CR1 |= I2C_CR1_STOP; - _i2c_wakeup_isr(i2cp); -} - -/** - * @brief DMA TX end IRQ handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_serve_tx_end_irq(I2CDriver *i2cp, uint32_t flags) { - I2C_TypeDef *dp = i2cp->i2c; - - /* DMA errors handling.*/ -#if defined(STM32_I2C_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2C_DMA_ERROR_HOOK(i2cp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(i2cp->dmatx); - /* Enables interrupts to catch BTF event meaning transmission part complete. - Interrupt handler will decide to generate STOP or to begin receiving part - of R/W transaction itself.*/ - dp->CR2 |= I2C_CR2_ITEVTEN; -} - -/** - * @brief I2C error handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] sr content of the SR1 register to be decoded - * - * @notapi - */ -static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr) { - - /* Clears interrupt flags just to be safe.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); - - i2cp->errors = I2C_NO_ERROR; - - if (sr & I2C_SR1_BERR) /* Bus error. */ - i2cp->errors |= I2C_BUS_ERROR; - - if (sr & I2C_SR1_ARLO) /* Arbitration lost. */ - i2cp->errors |= I2C_ARBITRATION_LOST; - - if (sr & I2C_SR1_AF) { /* Acknowledge fail. */ - i2cp->i2c->CR2 &= ~I2C_CR2_ITEVTEN; - i2cp->i2c->CR1 |= I2C_CR1_STOP; /* Setting stop bit. */ - i2cp->errors |= I2C_ACK_FAILURE; - } - - if (sr & I2C_SR1_OVR) /* Overrun. */ - i2cp->errors |= I2C_OVERRUN; - - if (sr & I2C_SR1_TIMEOUT) /* SMBus Timeout. */ - i2cp->errors |= I2C_TIMEOUT; - - if (sr & I2C_SR1_PECERR) /* PEC error. */ - i2cp->errors |= I2C_PEC_ERROR; - - if (sr & I2C_SR1_SMBALERT) /* SMBus alert. */ - i2cp->errors |= I2C_SMB_ALERT; - - /* If some error has been identified then sends wakes the waiting thread.*/ - if (i2cp->errors != I2C_NO_ERROR) - _i2c_wakeup_error_isr(i2cp); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -/** - * @brief I2C1 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - i2c_lld_serve_event_interrupt(&I2CD1); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief I2C1 error interrupt handler. - */ -OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { - uint16_t sr = I2CD1.i2c->SR1; - - OSAL_IRQ_PROLOGUE(); - - I2CD1.i2c->SR1 = ~(sr & I2C_ERROR_MASK); - i2c_lld_serve_error_interrupt(&I2CD1, sr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -/** - * @brief I2C2 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - i2c_lld_serve_event_interrupt(&I2CD2); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief I2C2 error interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { - uint16_t sr = I2CD2.i2c->SR1; - - OSAL_IRQ_PROLOGUE(); - - I2CD2.i2c->SR1 = ~(sr & I2C_ERROR_MASK); - i2c_lld_serve_error_interrupt(&I2CD2, sr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -/** - * @brief I2C3 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - i2c_lld_serve_event_interrupt(&I2CD3); - - OSAL_IRQ_EPILOGUE(); -} - -/** - * @brief I2C3 error interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { - uint16_t sr = I2CD3.i2c->SR1; - - OSAL_IRQ_PROLOGUE(); - - I2CD3.i2c->SR1 = ~(sr & I2C_ERROR_MASK); - i2c_lld_serve_error_interrupt(&I2CD3, sr); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_I2C_USE_I2C3 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2C driver initialization. - * - * @notapi - */ -void i2c_lld_init(void) { - -#if STM32_I2C_USE_I2C1 - i2cObjectInit(&I2CD1); - I2CD1.thread = NULL; - I2CD1.i2c = I2C1; - I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); - I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - i2cObjectInit(&I2CD2); - I2CD2.thread = NULL; - I2CD2.i2c = I2C2; - I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); - I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - i2cObjectInit(&I2CD3); - I2CD3.thread = NULL; - I2CD3.i2c = I2C3; - I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); - I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); -#endif /* STM32_I2C_USE_I2C3 */ -} - -/** - * @brief Configures and activates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_start(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - - i2cp->txdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DIR_M2P; - i2cp->rxdmamode = STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | - STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE | - STM32_DMA_CR_DIR_P2M; - - /* If in stopped state then enables the I2C and DMA clocks.*/ - if (i2cp->state == I2C_STOP) { - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - bool b; - - rccResetI2C1(); - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableI2C1(FALSE); - nvicEnableVector(I2C1_EV_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); - nvicEnableVector(I2C1_ER_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { - bool b; - - rccResetI2C2(); - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableI2C2(FALSE); - nvicEnableVector(I2C2_EV_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); - nvicEnableVector(I2C2_ER_IRQn, STM32_I2C_I2C2_IRQ_PRIORITY); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { - bool b; - - rccResetI2C3(); - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_rx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2c_lld_serve_tx_end_irq, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableI2C3(FALSE); - nvicEnableVector(I2C3_EV_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); - nvicEnableVector(I2C3_ER_IRQn, STM32_I2C_I2C3_IRQ_PRIORITY); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_I2C3 */ - } - - /* I2C registers pointed by the DMA.*/ - dmaStreamSetPeripheral(i2cp->dmarx, &dp->DR); - dmaStreamSetPeripheral(i2cp->dmatx, &dp->DR); - - /* Reset i2c peripheral.*/ - dp->CR1 = I2C_CR1_SWRST; - dp->CR1 = 0; - dp->CR2 = I2C_CR2_ITERREN | I2C_CR2_DMAEN; - - /* Setup I2C parameters.*/ - i2c_lld_set_clock(i2cp); - i2c_lld_set_opmode(i2cp); - - /* Ready to go.*/ - dp->CR1 |= I2C_CR1_PE; -} - -/** - * @brief Deactivates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_stop(I2CDriver *i2cp) { - - /* If not in stopped state then disables the I2C clock.*/ - if (i2cp->state != I2C_STOP) { - - /* I2C disable.*/ - i2c_lld_abort_operation(i2cp); - dmaStreamRelease(i2cp->dmatx); - dmaStreamRelease(i2cp->dmarx); - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - nvicDisableVector(I2C1_EV_IRQn); - nvicDisableVector(I2C1_ER_IRQn); - rccDisableI2C1(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { - nvicDisableVector(I2C2_EV_IRQn); - nvicDisableVector(I2C2_ER_IRQn); - rccDisableI2C2(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { - nvicDisableVector(I2C3_EV_IRQn); - nvicDisableVector(I2C3_ER_IRQn); - rccDisableI2C3(FALSE); - } -#endif - } -} - -/** - * @brief Receives data via the I2C bus as master. - * @details Number of receiving bytes must be more than 1 on STM32F1x. This is - * hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - I2C_TypeDef *dp = i2cp->i2c; - systime_t start, end; - -#if defined(STM32F1XX_I2C) - osalDbgCheck(rxbytes > 1); -#endif - - /* Resetting error flags for this transfer.*/ - i2cp->errors = I2C_NO_ERROR; - - /* Initializes driver fields, LSB = 1 -> receive.*/ - i2cp->addr = (addr << 1) | 0x01; - - /* Releases the lock from high level driver.*/ - osalSysUnlock(); - - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); - - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); - end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); - - /* Waits until BUSY flag is reset or, alternatively, for a timeout - condition.*/ - while (true) { - osalSysLock(); - - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ - if (!(dp->SR2 & I2C_SR2_BUSY) && !(dp->CR1 & I2C_CR1_STOP)) - break; - - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) - return MSG_TIMEOUT; - - osalSysUnlock(); - } - - /* Starts the operation.*/ - dp->CR2 |= I2C_CR2_ITEVTEN; - dp->CR1 |= I2C_CR1_START | I2C_CR1_ACK; - - /* Waits for the operation completion or a timeout.*/ - return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); -} - -/** - * @brief Transmits data via the I2C bus as master. - * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. - * This is hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[in] txbuf pointer to the transmit buffer - * @param[in] txbytes number of bytes to be transmitted - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - I2C_TypeDef *dp = i2cp->i2c; - systime_t start, end; - -#if defined(STM32F1XX_I2C) - osalDbgCheck((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))); -#endif - - /* Resetting error flags for this transfer.*/ - i2cp->errors = I2C_NO_ERROR; - - /* Initializes driver fields, LSB = 0 -> transmit.*/ - i2cp->addr = (addr << 1); - - /* Releases the lock from high level driver.*/ - osalSysUnlock(); - - /* TX DMA setup.*/ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, txbuf); - dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); - - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); - - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); - end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); - - /* Waits until BUSY flag is reset or, alternatively, for a timeout - condition.*/ - while (true) { - osalSysLock(); - - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ - if (!(dp->SR2 & I2C_SR2_BUSY) && !(dp->CR1 & I2C_CR1_STOP)) - break; - - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) - return MSG_TIMEOUT; - - osalSysUnlock(); - } - - /* Starts the operation.*/ - dp->CR2 |= I2C_CR2_ITEVTEN; - dp->CR1 |= I2C_CR1_START; - - /* Waits for the operation completion or a timeout.*/ - return osalThreadSuspendTimeoutS(&i2cp->thread, timeout); -} - -#endif /* HAL_USE_I2C */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.h deleted file mode 100644 index 5e3024b00..000000000 --- a/os/hal/ports/STM32/LLD/I2Cv1/i2c_lld.h +++ /dev/null @@ -1,513 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/I2Cv1/i2c_lld.h - * @brief STM32 I2C subsystem low level driver header. - * - * @addtogroup I2C - * @{ - */ - -#ifndef _I2C_LLD_H_ -#define _I2C_LLD_H_ - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Peripheral clock frequency. - */ -#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000) - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2C1 driver enable switch. - * @details If set to @p TRUE the support for I2C1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C1 FALSE -#endif - -/** - * @brief I2C2 driver enable switch. - * @details If set to @p TRUE the support for I2C2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C2 FALSE -#endif - -/** - * @brief I2C3 driver enable switch. - * @details If set to @p TRUE the support for I2C3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C3 FALSE -#endif - -/** - * @brief I2C timeout on busy condition in milliseconds. - */ -#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_I2C_BUSY_TIMEOUT 50 -#endif - -/** - * @brief I2C1 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C2 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C3 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_IRQ_PRIORITY 10 -#endif - -/** -* @brief I2C1 DMA priority (0..3|lowest..highest). -* @note The priority level is used for both the TX and RX DMA streams but -* because of the streams ordering the RX stream has always priority -* over the TX stream. -*/ -#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_DMA_PRIORITY 1 -#endif - -/** -* @brief I2C2 DMA priority (0..3|lowest..highest). -* @note The priority level is used for both the TX and RX DMA streams but -* because of the streams ordering the RX stream has always priority -* over the TX stream. -*/ -#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_DMA_PRIORITY 1 -#endif - -/** -* @brief I2C3 DMA priority (0..3|lowest..highest). -* @note The priority level is used for both the TX and RX DMA streams but -* because of the streams ordering the RX stream has always priority -* over the TX stream. -*/ -#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C DMA error hook. - * @note The default action for DMA errors is a system halt because DMA - * error can only happen because programming errors. - */ -#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") -#endif - -#if STM32_ADVANCED_DMA || defined(__DOXYGEN__) - -/** - * @brief DMA stream used for I2C1 RX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C1_RX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 0) -#endif - -/** - * @brief DMA stream used for I2C1 TX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C1_TX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) -#endif - -/** - * @brief DMA stream used for I2C2 RX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C2_RX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) -#endif - -/** - * @brief DMA stream used for I2C2 TX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C2_TX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) -#endif - -/** - * @brief DMA stream used for I2C3 RX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C3_RX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2) -#endif - -/** - * @brief DMA stream used for I2C3 TX operations. - * @note This option is only available on platforms with enhanced DMA. - */ -#if !defined(STM32_I2C_I2C3_TX_DMA_STREAM) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) -#endif - -#else /* !STM32_ADVANCED_DMA */ - -/* Fixed streams for platforms using the old DMA peripheral, the values are - valid for both STM32F1xx and STM32L1xx.*/ -#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7) -#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6) -#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5) -#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4) - -#endif /* !STM32_ADVANCED_DMA*/ - -/* Flag for the whole STM32F1XX family. */ -#if defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ - defined(STM32F10X_HD_VL) || defined(STM32F10X_LD) || \ - defined(STM32F10X_MD) || defined(STM32F10X_HD) || \ - defined(STM32F10X_XL) || defined(STM32F10X_CL) -#define STM32F1XX_I2C -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/** @brief error checks */ -#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 -#error "I2C1 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 -#error "I2C2 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 -#error "I2C3 not present in the selected device" -#endif - -#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && \ - !STM32_I2C_USE_I2C3 -#error "I2C driver activated but no I2C peripheral assigned" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C3" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C3" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) -#error "I2C1 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) -#error "I2C2 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 RX" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 TX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 RX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 TX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 RX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/* Check clock range. */ -#if defined(STM32F4XX) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 42) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32L1XX) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 32) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32F2XX) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 30) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32F10X_LD_VL) || defined(STM32F10X_MD_VL) || \ - defined(STM32F10X_HD_VL) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 24) -#error "I2C peripheral clock frequency out of range." -#endif - -#elif defined(STM32F10X_LD) || defined(STM32F10X_MD) || \ - defined(STM32F10X_HD) || defined(STM32F10X_XL) || \ - defined(STM32F10X_CL) -#if !(I2C_CLK_FREQ >= 2) && (I2C_CLK_FREQ <= 36) -#error "I2C peripheral clock frequency out of range." -#endif -#else -#error "unspecified, unsupported or invalid STM32 platform" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type representing an I2C address. - */ -typedef uint16_t i2caddr_t; - -/** - * @brief Type of I2C driver condition flags. - */ -typedef uint32_t i2cflags_t; - -/** - * @brief Supported modes for the I2C bus. - */ -typedef enum { - OPMODE_I2C = 1, - OPMODE_SMBUS_DEVICE = 2, - OPMODE_SMBUS_HOST = 3, -} i2copmode_t; - -/** - * @brief Supported duty cycle modes for the I2C bus. - */ -typedef enum { - STD_DUTY_CYCLE = 1, - FAST_DUTY_CYCLE_2 = 2, - FAST_DUTY_CYCLE_16_9 = 3, -} i2cdutycycle_t; - -/** - * @brief Type of I2C driver configuration structure. - */ -typedef struct { - /* End of the mandatory fields.*/ - i2copmode_t op_mode; /**< @brief Specifies the I2C mode. */ - uint32_t clock_speed; /**< @brief Specifies the clock frequency. - @note Must be set to a value lower - than 400kHz. */ - i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode - duty cycle. */ -} I2CConfig; - -/** - * @brief Type of a structure representing an I2C driver. - */ -typedef struct I2CDriver I2CDriver; - -/** - * @brief Structure representing an I2C driver. - */ -struct I2CDriver { - /** - * @brief Driver state. - */ - i2cstate_t state; - /** - * @brief Current configuration data. - */ - const I2CConfig *config; - /** - * @brief Error flags. - */ - i2cflags_t errors; -#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#endif /* I2C_USE_MUTUAL_EXCLUSION */ -#if defined(I2C_DRIVER_EXT_FIELDS) - I2C_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Thread waiting for I/O completion. - */ - thread_reference_t thread; - /** - * @brief Current slave address without R/W bit. - */ - i2caddr_t addr; - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; - /** - * @brief Receive DMA channel. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief Pointer to the I2Cx registers block. - */ - I2C_TypeDef *i2c; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Get errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -#if STM32_I2C_USE_I2C1 -extern I2CDriver I2CD1; -#endif - -#if STM32_I2C_USE_I2C2 -extern I2CDriver I2CD2; -#endif - -#if STM32_I2C_USE_I2C3 -extern I2CDriver I2CD3; -#endif -#endif /* !defined(__DOXYGEN__) */ - -#ifdef __cplusplus -extern "C" { -#endif - void i2c_lld_init(void); - void i2c_lld_start(I2CDriver *i2cp); - void i2c_lld_stop(I2CDriver *i2cp); - msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C */ - -#endif /* _I2C_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c new file mode 100644 index 000000000..c5fdc2f42 --- /dev/null +++ b/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.c @@ -0,0 +1,1156 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/I2Cv2/i2c_lld.c + * @brief STM32 I2C subsystem low level driver source. + * + * @addtogroup I2C + * @{ + */ + +#include "hal.h" + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if STM32_I2C_USE_DMA == TRUE +#define DMAMODE_COMMON \ + (STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | \ + STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | \ + STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE) + +#define I2C1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_CHN) + +#define I2C1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_CHN) + +#define I2C2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_CHN) + +#define I2C2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_CHN) + +#define I2C3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_CHN) + +#define I2C3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_CHN) + +#define I2C4_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_RX_DMA_STREAM, \ + STM32_I2C4_RX_DMA_CHN) + +#define I2C4_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_TX_DMA_STREAM, \ + STM32_I2C4_TX_DMA_CHN) +#endif /* STM32_I2C_USE_DMA == TRUE */ + +#if STM32_I2C_USE_DMA == TRUE +#define i2c_lld_get_rxbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmarx) +#define i2c_lld_get_txbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmatx) +#else +#define i2c_lld_get_rxbytes(i2cp) (i2cp)->rxbytes +#define i2c_lld_get_txbytes(i2cp) (i2cp)->txbytes +#endif + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define I2C_ERROR_MASK \ + ((uint32_t)(I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | I2C_ISR_PECERR | \ + I2C_ISR_TIMEOUT | I2C_ISR_ALERT)) + +#define I2C_INT_MASK \ + ((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \ + I2C_ISR_ADDR | I2C_ISR_RXNE | I2C_ISR_TXIS)) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2C1 driver identifier.*/ +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +I2CDriver I2CD1; +#endif + +/** @brief I2C2 driver identifier.*/ +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +I2CDriver I2CD2; +#endif + +/** @brief I2C3 driver identifier.*/ +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +I2CDriver I2CD3; +#endif + +/** @brief I2C4 driver identifier.*/ +#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) +I2CDriver I2CD4; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Slave address setup. + * @note The RW bit is set to zero internally. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * + * @notapi + */ +static void i2c_lld_set_address(I2CDriver *i2cp, i2caddr_t addr) { + I2C_TypeDef *dp = i2cp->i2c; + + /* Address alignment depends on the addressing mode selected.*/ + if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0U) + dp->CR2 = (uint32_t)addr << 1U; + else + dp->CR2 = (uint32_t)addr; +} + +/** + * @brief I2C RX transfer setup. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t reload; + size_t n; + + /* The unit can transfer 255 bytes maximum in a single operation.*/ + n = i2c_lld_get_rxbytes(i2cp); + if (n > 255U) { + n = 255U; + reload = I2C_CR2_RELOAD; + } + else { + reload = 0U; + } + + /* Configures the CR2 registers with both the calculated and static + settings.*/ + dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | + I2C_CR2_RD_WRN | (n << 16U) | reload; +} + +/** + * @brief I2C TX transfer setup. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_setup_tx_transfer(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + uint32_t reload; + size_t n; + + /* The unit can transfer 255 bytes maximum in a single operation.*/ + n = i2c_lld_get_txbytes(i2cp); + if (n > 255U) { + n = 255U; + reload = I2C_CR2_RELOAD; + } + else { + reload = 0U; + } + + /* Configures the CR2 registers with both the calculated and static + settings.*/ + dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | + (n << 16U) | reload; +} + +/** + * @brief Aborts an I2C transaction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +static void i2c_lld_abort_operation(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + + if (dp->CR1 & I2C_CR1_PE) { + /* Stops the I2C peripheral.*/ + dp->CR1 &= ~I2C_CR1_PE; + while (dp->CR1 & I2C_CR1_PE) + dp->CR1 &= ~I2C_CR1_PE; + dp->CR1 |= I2C_CR1_PE; + } + +#if STM32_I2C_USE_DMA == TRUE + /* Stops the associated DMA streams.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); +#else + dp->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); +#endif +} + +/** + * @brief I2C shared ISR code. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] isr content of the ISR register to be decoded + * + * @notapi + */ +static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { + I2C_TypeDef *dp = i2cp->i2c; + + /* Special case of a received NACK, the transfer is aborted.*/ + if ((isr & I2C_ISR_NACKF) != 0U) { +#if STM32_I2C_USE_DMA == TRUE + /* Stops the associated DMA streams.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); +#endif + + /* Error flag.*/ + i2cp->errors |= I2C_ACK_FAILURE; + + /* Transaction finished sending the STOP.*/ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more interrupts.*/ + dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE); + + /* Errors are signaled to the upper layer.*/ + _i2c_wakeup_error_isr(i2cp); + + return; + } + +#if STM32_I2C_USE_DMA == FALSE + /* Handling of data transfer if the DMA mode is disabled.*/ + { + uint32_t cr1 = dp->CR1; + + if (i2cp->state == I2C_ACTIVE_TX) { + /* Transmission phase.*/ + if (((cr1 &I2C_CR1_TXIE) != 0U) && ((isr & I2C_ISR_TXIS) != 0U)) { + dp->TXDR = (uint32_t)*i2cp->txptr; + i2cp->txptr++; + i2cp->txbytes--; + if (i2cp->txbytes == 0U) { + dp->CR1 &= ~I2C_CR1_TXIE; + } + } + } + else { + /* Receive phase.*/ + if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { + *i2cp->rxptr = (uint8_t)dp->RXDR; + i2cp->rxptr++; + i2cp->rxbytes--; + if (i2cp->rxbytes == 0U) { + dp->CR1 &= ~I2C_CR1_RXIE; + } + } + } + } +#endif + + /* Partial transfer handling, restarting the transfer and returning.*/ + if ((isr & I2C_ISR_TCR) != 0U) { + if (i2cp->state == I2C_ACTIVE_TX) { + i2c_lld_setup_tx_transfer(i2cp); + } + else { + i2c_lld_setup_rx_transfer(i2cp); + } + return; + } + + /* The following condition is true if a transfer phase has been completed.*/ + if ((isr & I2C_ISR_TC) != 0U) { + if (i2cp->state == I2C_ACTIVE_TX) { + /* End of the transmit phase.*/ + +#if STM32_I2C_USE_DMA == TRUE + /* Disabling TX DMA channel.*/ + dmaStreamDisable(i2cp->dmatx); +#endif + + /* Starting receive phase if necessary.*/ + if (i2c_lld_get_rxbytes(i2cp) > 0U) { + /* Setting up the peripheral.*/ + i2c_lld_setup_rx_transfer(i2cp); + +#if STM32_I2C_USE_DMA == TRUE + /* Enabling RX DMA.*/ + dmaStreamEnable(i2cp->dmarx); +#else + /* RX interrupt enabled.*/ + dp->CR1 |= I2C_CR1_RXIE; +#endif + + /* Starts the read operation.*/ + dp->CR2 |= I2C_CR2_START; + + /* State change.*/ + i2cp->state = I2C_ACTIVE_RX; + + /* Note, returning because the transaction is not over yet.*/ + return; + } + } + else { + /* End of the receive phase.*/ +#if STM32_I2C_USE_DMA == TRUE + /* Disabling RX DMA channel.*/ + dmaStreamDisable(i2cp->dmarx); +#endif + } + + /* Transaction finished sending the STOP.*/ + dp->CR2 |= I2C_CR2_STOP; + + /* Make sure no more 'Transfer Complete' interrupts.*/ + dp->CR1 &= ~I2C_CR1_TCIE; + + /* Normal transaction end.*/ + _i2c_wakeup_isr(i2cp); + } +} + +/** + * @brief I2C error handler. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] isr content of the ISR register to be decoded + * + * @notapi + */ +static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { + +#if STM32_I2C_USE_DMA == TRUE + /* Clears DMA interrupt flags just to be safe.*/ + dmaStreamDisable(i2cp->dmatx); + dmaStreamDisable(i2cp->dmarx); +#else + /* Disabling RX and TX interrupts.*/ + i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); +#endif + + if (isr & I2C_ISR_BERR) + i2cp->errors |= I2C_BUS_ERROR; + + if (isr & I2C_ISR_ARLO) + i2cp->errors |= I2C_ARBITRATION_LOST; + + if (isr & I2C_ISR_OVR) + i2cp->errors |= I2C_OVERRUN; + + if (isr & I2C_ISR_TIMEOUT) + i2cp->errors |= I2C_TIMEOUT; + + /* If some error has been identified then sends wakes the waiting thread.*/ + if (i2cp->errors != I2C_NO_ERROR) + _i2c_wakeup_error_isr(i2cp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) +#if defined(STM32_I2C1_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C1 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C1_GLOBAL_HANDLER) { + uint32_t isr = I2CD1.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD1.i2c->ICR = isr; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD1, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD1, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C1_EVENT_HANDLER) && defined(STM32_I2C1_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { + uint32_t isr = I2CD1.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD1.i2c->ICR = isr & I2C_INT_MASK; + + i2c_lld_serve_interrupt(&I2CD1, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { + uint32_t isr = I2CD1.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD1.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD1, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C1 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) +#if defined(STM32_I2C2_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C2 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C2_GLOBAL_HANDLER) { + uint32_t isr = I2CD2.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD2.i2c->ICR = isr; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD2, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD2, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C2_EVENT_HANDLER) && defined(STM32_I2C2_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { + uint32_t isr = I2CD2.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD2.i2c->ICR = isr & I2C_INT_MASK; + + i2c_lld_serve_interrupt(&I2CD2, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { + uint32_t isr = I2CD2.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD2.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD2, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C2 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) +#if defined(STM32_I2C3_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C3 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C3_GLOBAL_HANDLER) { + uint32_t isr = I2CD3.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD3.i2c->ICR = isr; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD3, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C3_EVENT_HANDLER) && defined(STM32_I2C3_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { + uint32_t isr = I2CD3.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD3.i2c->ICR = isr & I2C_INT_MASK; + + i2c_lld_serve_interrupt(&I2CD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { + uint32_t isr = I2CD3.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD3.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD3, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C3 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C3 */ + +#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) +#if defined(STM32_I2C4_GLOBAL_HANDLER) || defined(__DOXYGEN__) +/** + * @brief I2C4 event interrupt handler. + * + * @notapi + */ +OSAL_IRQ_HANDLER(STM32_I2C4_GLOBAL_HANDLER) { + uint32_t isr = I2CD4.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD4.i2c->ICR = isr; + + if (isr & I2C_ERROR_MASK) + i2c_lld_serve_error_interrupt(&I2CD4, isr); + else if (isr & I2C_INT_MASK) + i2c_lld_serve_interrupt(&I2CD4, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#elif defined(STM32_I2C4_EVENT_HANDLER) && defined(STM32_I2C4_ERROR_HANDLER) +OSAL_IRQ_HANDLER(STM32_I2C4_EVENT_HANDLER) { + uint32_t isr = I2CD4.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD4.i2c->ICR = isr & I2C_INT_MASK; + + i2c_lld_serve_interrupt(&I2CD4, isr); + + OSAL_IRQ_EPILOGUE(); +} + +OSAL_IRQ_HANDLER(STM32_I2C4_ERROR_HANDLER) { + uint32_t isr = I2CD4.i2c->ISR; + + OSAL_IRQ_PROLOGUE(); + + /* Clearing IRQ bits.*/ + I2CD4.i2c->ICR = isr & I2C_ERROR_MASK; + + i2c_lld_serve_error_interrupt(&I2CD4, isr); + + OSAL_IRQ_EPILOGUE(); +} + +#else +#error "I2C4 interrupt handlers not defined" +#endif +#endif /* STM32_I2C_USE_I2C4 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2C driver initialization. + * + * @notapi + */ +void i2c_lld_init(void) { + +#if STM32_I2C_USE_I2C1 + i2cObjectInit(&I2CD1); + I2CD1.thread = NULL; + I2CD1.i2c = I2C1; +#if STM32_I2C_USE_DMA == TRUE + I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); + I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + i2cObjectInit(&I2CD2); + I2CD2.thread = NULL; + I2CD2.i2c = I2C2; +#if STM32_I2C_USE_DMA == TRUE + I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); + I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + i2cObjectInit(&I2CD3); + I2CD3.thread = NULL; + I2CD3.i2c = I2C3; +#if STM32_I2C_USE_DMA == TRUE + I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); + I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C3 */ + +#if STM32_I2C_USE_I2C4 + i2cObjectInit(&I2CD4); + I2CD4.thread = NULL; + I2CD4.i2c = I2C4; +#if STM32_I2C_USE_DMA == TRUE + I2CD4.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C4_RX_DMA_STREAM); + I2CD4.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C4_TX_DMA_STREAM); +#endif +#endif /* STM32_I2C_USE_I2C4 */ +} + +/** + * @brief Configures and activates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_start(I2CDriver *i2cp) { + I2C_TypeDef *dp = i2cp->i2c; + +#if STM32_I2C_USE_DMA == TRUE + /* Common DMA modes.*/ + i2cp->txdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_M2P; + i2cp->rxdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_P2M; +#endif + + /* Make sure I2C peripheral is disabled */ + dp->CR1 &= ~I2C_CR1_PE; + + /* If in stopped state then enables the I2C and DMA clocks.*/ + if (i2cp->state == I2C_STOP) { + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { + + rccResetI2C1(); + rccEnableI2C1(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C1_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C1_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); + } +#endif /* STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C1_GLOBAL_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); +#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) + nvicEnableVector(STM32_I2C1_EVENT_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C1_ERROR_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); +#else +#error "I2C1 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C1 */ + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { + + rccResetI2C2(); + rccEnableI2C2(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C2_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C2_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C2_GLOBAL_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); +#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) + nvicEnableVector(STM32_I2C2_EVENT_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C2_ERROR_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); +#else +#error "I2C2 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C2 */ + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { + + rccResetI2C3(); + rccEnableI2C3(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C3_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C3_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C3_GLOBAL_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); +#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) + nvicEnableVector(STM32_I2C3_EVENT_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C3_ERROR_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); +#else +#error "I2C3 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C3 */ + +#if STM32_I2C_USE_I2C4 + if (&I2CD4 == i2cp) { + + rccResetI2C4(); + rccEnableI2C4(FALSE); +#if STM32_I2C_USE_DMA == TRUE + { + bool b; + + b = dmaStreamAllocate(i2cp->dmarx, + STM32_I2C_I2C4_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(i2cp->dmatx, + STM32_I2C_I2C4_IRQ_PRIORITY, + NULL, + (void *)i2cp); + osalDbgAssert(!b, "stream already allocated"); + + i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C4_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); + i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C4_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); + } +#endif /*STM32_I2C_USE_DMA == TRUE */ + +#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicEnableVector(STM32_I2C4_GLOBAL_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); +#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) + nvicEnableVector(STM32_I2C4_EVENT_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); + nvicEnableVector(STM32_I2C4_ERROR_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); +#else +#error "I2C4 interrupt numbers not defined" +#endif + } +#endif /* STM32_I2C_USE_I2C4 */ + } + +#if STM32_I2C_USE_DMA == TRUE + /* I2C registers pointed by the DMA.*/ + dmaStreamSetPeripheral(i2cp->dmarx, &dp->RXDR); + dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR); +#endif + + /* Reset i2c peripheral, the TCIE bit will be handled separately.*/ + dp->CR1 = i2cp->config->cr1 | +#if STM32_I2C_USE_DMA == TRUE + I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN | /* Enable only if using DMA */ +#endif + I2C_CR1_ERRIE | I2C_CR1_NACKIE; + + /* Setup I2C parameters.*/ + dp->TIMINGR = i2cp->config->timingr; + + /* Ready to go.*/ + dp->CR1 |= I2C_CR1_PE; +} + +/** + * @brief Deactivates the I2C peripheral. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +void i2c_lld_stop(I2CDriver *i2cp) { + + /* If not in stopped state then disables the I2C clock.*/ + if (i2cp->state != I2C_STOP) { + + /* I2C disable.*/ + i2c_lld_abort_operation(i2cp); +#if STM32_I2C_USE_DMA == TRUE + dmaStreamRelease(i2cp->dmatx); + dmaStreamRelease(i2cp->dmarx); +#endif + +#if STM32_I2C_USE_I2C1 + if (&I2CD1 == i2cp) { +#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C1_GLOBAL_NUMBER); +#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) + nvicDisableVector(STM32_I2C1_EVENT_NUMBER); + nvicDisableVector(STM32_I2C1_ERROR_NUMBER); +#else +#error "I2C1 interrupt numbers not defined" +#endif + + rccDisableI2C1(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C2 + if (&I2CD2 == i2cp) { +#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C2_GLOBAL_NUMBER); +#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) + nvicDisableVector(STM32_I2C2_EVENT_NUMBER); + nvicDisableVector(STM32_I2C2_ERROR_NUMBER); +#else +#error "I2C2 interrupt numbers not defined" +#endif + + rccDisableI2C2(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C3 + if (&I2CD3 == i2cp) { +#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C3_GLOBAL_NUMBER); +#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) + nvicDisableVector(STM32_I2C3_EVENT_NUMBER); + nvicDisableVector(STM32_I2C3_ERROR_NUMBER); +#else +#error "I2C3 interrupt numbers not defined" +#endif + + rccDisableI2C3(FALSE); + } +#endif + +#if STM32_I2C_USE_I2C4 + if (&I2CD4 == i2cp) { +#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) + nvicDisableVector(STM32_I2C4_GLOBAL_NUMBER); +#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) + nvicDisableVector(STM32_I2C4_EVENT_NUMBER); + nvicDisableVector(STM32_I2C4_ERROR_NUMBER); +#else +#error "I2C4 interrupt numbers not defined" +#endif + + rccDisableI2C4(FALSE); + } +#endif + } +} + +/** + * @brief Receives data via the I2C bus as master. + * @details Number of receiving bytes must be more than 1 on STM32F1x. This is + * hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + msg_t msg; + I2C_TypeDef *dp = i2cp->i2c; + systime_t start, end; + + /* Resetting error flags for this transfer.*/ + i2cp->errors = I2C_NO_ERROR; + + /* Releases the lock from high level driver.*/ + osalSysUnlock(); + +#if STM32_I2C_USE_DMA == TRUE + /* RX DMA setup.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#else + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; +#endif + + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); + end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); + + /* Waits until BUSY flag is reset or, alternatively, for a timeout + condition.*/ + while (true) { + osalSysLock(); + + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ + if ((dp->ISR & I2C_ISR_BUSY) == 0) + break; + + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { + return MSG_TIMEOUT; + } + + osalSysUnlock(); + } + + /* Setting up the slave address.*/ + i2c_lld_set_address(i2cp, addr); + + /* Setting up the peripheral.*/ + i2c_lld_setup_rx_transfer(i2cp); + +#if STM32_I2C_USE_DMA == TRUE + /* Enabling RX DMA.*/ + dmaStreamEnable(i2cp->dmarx); + + /* Transfer complete interrupt enabled.*/ + dp->CR1 |= I2C_CR1_TCIE; +#else + + /* Transfer complete and RX interrupts enabled.*/ + dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; +#endif + + /* Starts the operation.*/ + dp->CR2 |= I2C_CR2_START; + + /* Waits for the operation completion or a timeout.*/ + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + + /* In case of a software timeout a STOP is sent as an extreme attempt + to release the bus.*/ + if (msg == MSG_TIMEOUT) { + dp->CR2 |= I2C_CR2_STOP; + } + + return msg; +} + +/** + * @brief Transmits data via the I2C bus as master. + * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. + * This is hardware restriction. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * @param[in] addr slave device address + * @param[in] txbuf pointer to the transmit buffer + * @param[in] txbytes number of bytes to be transmitted + * @param[out] rxbuf pointer to the receive buffer + * @param[in] rxbytes number of bytes to be received + * @param[in] timeout the number of ticks before the operation timeouts, + * the following special values are allowed: + * - @a TIME_INFINITE no timeout. + * . + * @return The operation status. + * @retval MSG_OK if the function succeeded. + * @retval MSG_RESET if one or more I2C errors occurred, the errors can + * be retrieved using @p i2cGetErrors(). + * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a + * timeout the driver must be stopped and restarted + * because the bus is in an uncertain state. + * + * @notapi + */ +msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout) { + msg_t msg; + I2C_TypeDef *dp = i2cp->i2c; + systime_t start, end; + + /* Resetting error flags for this transfer.*/ + i2cp->errors = I2C_NO_ERROR; + + /* Releases the lock from high level driver.*/ + osalSysUnlock(); + +#if STM32_I2C_USE_DMA == TRUE + /* TX DMA setup.*/ + dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); + dmaStreamSetMemory0(i2cp->dmatx, txbuf); + dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); + + /* RX DMA setup, note, rxbytes can be zero but we write the value anyway.*/ + dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); + dmaStreamSetMemory0(i2cp->dmarx, rxbuf); + dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); +#else + i2cp->txptr = txbuf; + i2cp->txbytes = txbytes; + i2cp->rxptr = rxbuf; + i2cp->rxbytes = rxbytes; +#endif + + /* Calculating the time window for the timeout on the busy bus condition.*/ + start = osalOsGetSystemTimeX(); + end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); + + /* Waits until BUSY flag is reset or, alternatively, for a timeout + condition.*/ + while (true) { + osalSysLock(); + + /* If the bus is not busy then the operation can continue, note, the + loop is exited in the locked state.*/ + if ((dp->ISR & I2C_ISR_BUSY) == 0) + break; + + /* If the system time went outside the allowed window then a timeout + condition is returned.*/ + if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { + return MSG_TIMEOUT; + } + + osalSysUnlock(); + } + + /* Setting up the slave address.*/ + i2c_lld_set_address(i2cp, addr); + + /* Preparing the transfer.*/ + i2c_lld_setup_tx_transfer(i2cp); + +#if STM32_I2C_USE_DMA == TRUE + /* Enabling TX DMA.*/ + dmaStreamEnable(i2cp->dmatx); + + /* Transfer complete interrupt enabled.*/ + dp->CR1 |= I2C_CR1_TCIE; +#else + /* Transfer complete and TX interrupts enabled.*/ + dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; +#endif + + /* Starts the operation.*/ + dp->CR2 |= I2C_CR2_START; + + /* Waits for the operation completion or a timeout.*/ + msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); + + /* In case of a software timeout a STOP is sent as an extreme attempt + to release the bus.*/ + if (msg == MSG_TIMEOUT) { + dp->CR2 |= I2C_CR2_STOP; + } + + return msg; +} + +#endif /* HAL_USE_I2C */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.h new file mode 100644 index 000000000..eede83103 --- /dev/null +++ b/os/hal/ports/STM32/LLD/I2Cv2/hal_i2c_lld.h @@ -0,0 +1,502 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/I2Cv2/i2c_lld.h + * @brief STM32 I2C subsystem low level driver header. + * + * @addtogroup I2C + * @{ + */ + +#ifndef _I2C_LLD_H_ +#define _I2C_LLD_H_ + +#if HAL_USE_I2C || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name TIMINGR register definitions + * @{ + */ +#define STM32_TIMINGR_PRESC_MASK (15U << 28) +#define STM32_TIMINGR_PRESC(n) ((n) << 28) +#define STM32_TIMINGR_SCLDEL_MASK (15U << 20) +#define STM32_TIMINGR_SCLDEL(n) ((n) << 20) +#define STM32_TIMINGR_SDADEL_MASK (15U << 16) +#define STM32_TIMINGR_SDADEL(n) ((n) << 16) +#define STM32_TIMINGR_SCLH_MASK (255U << 8) +#define STM32_TIMINGR_SCLH(n) ((n) << 8) +#define STM32_TIMINGR_SCLL_MASK (255U << 0) +#define STM32_TIMINGR_SCLL(n) ((n) << 0) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2C1 driver enable switch. + * @details If set to @p TRUE the support for I2C1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C1 FALSE +#endif + +/** + * @brief I2C2 driver enable switch. + * @details If set to @p TRUE the support for I2C2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C2 FALSE +#endif + +/** + * @brief I2C3 driver enable switch. + * @details If set to @p TRUE the support for I2C3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C3 FALSE +#endif + +/** + * @brief I2C4 driver enable switch. + * @details If set to @p TRUE the support for I2C4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_I2C_USE_I2C4) || defined(__DOXYGEN__) +#define STM32_I2C_USE_I2C4 FALSE +#endif + +/** + * @brief I2C timeout on busy condition in milliseconds. + */ +#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_I2C_BUSY_TIMEOUT 50 +#endif + +/** + * @brief I2C1 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C2 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C3 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2C4 interrupt priority level setting. + */ +#if !defined(STM32_I2C_I2C4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C4_IRQ_PRIORITY 10 +#endif + +/** + * @brief DMA use switch. + */ +#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__) +#define STM32_I2C_USE_DMA TRUE +#endif + +/** + * @brief I2C1 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C1_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C2 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C2_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C3 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C3_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C4 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_I2C_I2C4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2C_I2C4_DMA_PRIORITY 1 +#endif + +/** + * @brief I2C DMA error hook. + * @note The default action for DMA errors is a system halt because DMA + * error can only happen because programming errors. + */ +#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/** @brief error checks */ +#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 +#error "I2C1 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 +#error "I2C2 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 +#error "I2C3 not present in the selected device" +#endif + +#if STM32_I2C_USE_I2C4 && !STM32_HAS_I2C4 +#error "I2C4 not present in the selected device" +#endif + +#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && !STM32_I2C_USE_I2C3 && \ + !STM32_I2C_USE_I2C4 +#error "I2C driver activated but no I2C peripheral assigned" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C3" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to I2C4" +#endif + +#if STM32_I2C_USE_DMA == TRUE +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C1" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C2" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C3" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to I2C4" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) +#error "I2C1 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) +#error "I2C2 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C3 && (!defined(STM32_I2C_I2C3_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C3_TX_DMA_STREAM)) +#error "I2C3 DMA streams not defined" +#endif + +#if STM32_I2C_USE_I2C4 && (!defined(STM32_I2C_I2C4_RX_DMA_STREAM) || \ + !defined(STM32_I2C_I2C4_TX_DMA_STREAM)) +#error "I2C4 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ + STM32_I2C1_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 RX" +#endif + +#if STM32_I2C_USE_I2C1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ + STM32_I2C1_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C1 TX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ + STM32_I2C2_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 RX" +#endif + +#if STM32_I2C_USE_I2C2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ + STM32_I2C2_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C2 TX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ + STM32_I2C3_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 RX" +#endif + +#if STM32_I2C_USE_I2C3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ + STM32_I2C3_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C3 TX" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_RX_DMA_STREAM, \ + STM32_I2C4_RX_DMA_MSK) +#error "invalid DMA stream associated to I2C4 RX" +#endif + +#if STM32_I2C_USE_I2C4 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_TX_DMA_STREAM, \ + STM32_I2C4_TX_DMA_MSK) +#error "invalid DMA stream associated to I2C4 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif +#endif /* STM32_I2C_USE_DMA == TRUE */ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type representing an I2C address. + */ +typedef uint16_t i2caddr_t; + +/** + * @brief Type of I2C driver condition flags. + */ +typedef uint32_t i2cflags_t; + +/** + * @brief Type of I2C driver configuration structure. + */ +typedef struct { + /** + * @brief TIMINGR register initialization. + * @note Refer to the STM32 reference manual, the values are affected + * by the system clock settings in mcuconf.h. + */ + uint32_t timingr; + /** + * @brief CR1 register initialization. + * @note Leave to zero unless you know what you are doing. + */ + uint32_t cr1; + /** + * @brief CR2 register initialization. + * @note Only the ADD10 bit can eventually be specified here. + */ + uint32_t cr2; +} I2CConfig; + +/** + * @brief Type of a structure representing an I2C driver. + */ +typedef struct I2CDriver I2CDriver; + +/** + * @brief Structure representing an I2C driver. + */ +struct I2CDriver { + /** + * @brief Driver state. + */ + i2cstate_t state; + /** + * @brief Current configuration data. + */ + const I2CConfig *config; + /** + * @brief Error flags. + */ + i2cflags_t errors; +#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + mutex_t mutex; +#endif /* I2C_USE_MUTUAL_EXCLUSION */ +#if defined(I2C_DRIVER_EXT_FIELDS) + I2C_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion. + */ + thread_reference_t thread; +#if (STM32_I2C_USE_DMA == TRUE) || defined(__DOXYGEN__) + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; + /** + * @brief Receive DMA channel. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dmatx; +#else /* STM32_I2C_USE_DMA == FALSE */ + /** + * @brief Pointer to the next TX buffer location. + */ + const uint8_t *txptr; + /** + * @brief Number of bytes in TX phase. + */ + size_t txbytes; + /** + * @brief Pointer to the next RX buffer location. + */ + uint8_t *rxptr; + /** + * @brief Number of bytes in RX phase. + */ + size_t rxbytes; +#endif /* STM32_I2C_USE_DMA == FALSE */ + /** + * @brief Pointer to the I2Cx registers block. + */ + I2C_TypeDef *i2c; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Get errors from I2C driver. + * + * @param[in] i2cp pointer to the @p I2CDriver object + * + * @notapi + */ +#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +#if STM32_I2C_USE_I2C1 +extern I2CDriver I2CD1; +#endif + +#if STM32_I2C_USE_I2C2 +extern I2CDriver I2CD2; +#endif + +#if STM32_I2C_USE_I2C3 +extern I2CDriver I2CD3; +#endif + +#if STM32_I2C_USE_I2C4 +extern I2CDriver I2CD4; +#endif + +#endif /* !defined(__DOXYGEN__) */ + +#ifdef __cplusplus +extern "C" { +#endif + void i2c_lld_init(void); + void i2c_lld_start(I2CDriver *i2cp); + void i2c_lld_stop(I2CDriver *i2cp); + msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, + const uint8_t *txbuf, size_t txbytes, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); + msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, + uint8_t *rxbuf, size_t rxbytes, + systime_t timeout); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2C */ + +#endif /* _I2C_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c deleted file mode 100644 index c5fdc2f42..000000000 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/I2Cv2/i2c_lld.c - * @brief STM32 I2C subsystem low level driver source. - * - * @addtogroup I2C - * @{ - */ - -#include "hal.h" - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if STM32_I2C_USE_DMA == TRUE -#define DMAMODE_COMMON \ - (STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE | \ - STM32_DMA_CR_MINC | STM32_DMA_CR_DMEIE | \ - STM32_DMA_CR_TEIE | STM32_DMA_CR_TCIE) - -#define I2C1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_CHN) - -#define I2C1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_CHN) - -#define I2C2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_CHN) - -#define I2C2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_CHN) - -#define I2C3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_CHN) - -#define I2C3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_CHN) - -#define I2C4_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_RX_DMA_STREAM, \ - STM32_I2C4_RX_DMA_CHN) - -#define I2C4_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2C_I2C4_TX_DMA_STREAM, \ - STM32_I2C4_TX_DMA_CHN) -#endif /* STM32_I2C_USE_DMA == TRUE */ - -#if STM32_I2C_USE_DMA == TRUE -#define i2c_lld_get_rxbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmarx) -#define i2c_lld_get_txbytes(i2cp) dmaStreamGetTransactionSize((i2cp)->dmatx) -#else -#define i2c_lld_get_rxbytes(i2cp) (i2cp)->rxbytes -#define i2c_lld_get_txbytes(i2cp) (i2cp)->txbytes -#endif - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -#define I2C_ERROR_MASK \ - ((uint32_t)(I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR | I2C_ISR_PECERR | \ - I2C_ISR_TIMEOUT | I2C_ISR_ALERT)) - -#define I2C_INT_MASK \ - ((uint32_t)(I2C_ISR_TCR | I2C_ISR_TC | I2C_ISR_STOPF | I2C_ISR_NACKF | \ - I2C_ISR_ADDR | I2C_ISR_RXNE | I2C_ISR_TXIS)) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief I2C1 driver identifier.*/ -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -I2CDriver I2CD1; -#endif - -/** @brief I2C2 driver identifier.*/ -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -I2CDriver I2CD2; -#endif - -/** @brief I2C3 driver identifier.*/ -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -I2CDriver I2CD3; -#endif - -/** @brief I2C4 driver identifier.*/ -#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) -I2CDriver I2CD4; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Slave address setup. - * @note The RW bit is set to zero internally. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * - * @notapi - */ -static void i2c_lld_set_address(I2CDriver *i2cp, i2caddr_t addr) { - I2C_TypeDef *dp = i2cp->i2c; - - /* Address alignment depends on the addressing mode selected.*/ - if ((i2cp->config->cr2 & I2C_CR2_ADD10) == 0U) - dp->CR2 = (uint32_t)addr << 1U; - else - dp->CR2 = (uint32_t)addr; -} - -/** - * @brief I2C RX transfer setup. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_setup_rx_transfer(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint32_t reload; - size_t n; - - /* The unit can transfer 255 bytes maximum in a single operation.*/ - n = i2c_lld_get_rxbytes(i2cp); - if (n > 255U) { - n = 255U; - reload = I2C_CR2_RELOAD; - } - else { - reload = 0U; - } - - /* Configures the CR2 registers with both the calculated and static - settings.*/ - dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | - I2C_CR2_RD_WRN | (n << 16U) | reload; -} - -/** - * @brief I2C TX transfer setup. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_setup_tx_transfer(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - uint32_t reload; - size_t n; - - /* The unit can transfer 255 bytes maximum in a single operation.*/ - n = i2c_lld_get_txbytes(i2cp); - if (n > 255U) { - n = 255U; - reload = I2C_CR2_RELOAD; - } - else { - reload = 0U; - } - - /* Configures the CR2 registers with both the calculated and static - settings.*/ - dp->CR2 = (dp->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD)) | i2cp->config->cr2 | - (n << 16U) | reload; -} - -/** - * @brief Aborts an I2C transaction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -static void i2c_lld_abort_operation(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - - if (dp->CR1 & I2C_CR1_PE) { - /* Stops the I2C peripheral.*/ - dp->CR1 &= ~I2C_CR1_PE; - while (dp->CR1 & I2C_CR1_PE) - dp->CR1 &= ~I2C_CR1_PE; - dp->CR1 |= I2C_CR1_PE; - } - -#if STM32_I2C_USE_DMA == TRUE - /* Stops the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); -#else - dp->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); -#endif -} - -/** - * @brief I2C shared ISR code. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] isr content of the ISR register to be decoded - * - * @notapi - */ -static void i2c_lld_serve_interrupt(I2CDriver *i2cp, uint32_t isr) { - I2C_TypeDef *dp = i2cp->i2c; - - /* Special case of a received NACK, the transfer is aborted.*/ - if ((isr & I2C_ISR_NACKF) != 0U) { -#if STM32_I2C_USE_DMA == TRUE - /* Stops the associated DMA streams.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); -#endif - - /* Error flag.*/ - i2cp->errors |= I2C_ACK_FAILURE; - - /* Transaction finished sending the STOP.*/ - dp->CR2 |= I2C_CR2_STOP; - - /* Make sure no more interrupts.*/ - dp->CR1 &= ~(I2C_CR1_TCIE | I2C_CR1_TXIE | I2C_CR1_RXIE); - - /* Errors are signaled to the upper layer.*/ - _i2c_wakeup_error_isr(i2cp); - - return; - } - -#if STM32_I2C_USE_DMA == FALSE - /* Handling of data transfer if the DMA mode is disabled.*/ - { - uint32_t cr1 = dp->CR1; - - if (i2cp->state == I2C_ACTIVE_TX) { - /* Transmission phase.*/ - if (((cr1 &I2C_CR1_TXIE) != 0U) && ((isr & I2C_ISR_TXIS) != 0U)) { - dp->TXDR = (uint32_t)*i2cp->txptr; - i2cp->txptr++; - i2cp->txbytes--; - if (i2cp->txbytes == 0U) { - dp->CR1 &= ~I2C_CR1_TXIE; - } - } - } - else { - /* Receive phase.*/ - if (((cr1 & I2C_CR1_RXIE) != 0U) && ((isr & I2C_ISR_RXNE) != 0U)) { - *i2cp->rxptr = (uint8_t)dp->RXDR; - i2cp->rxptr++; - i2cp->rxbytes--; - if (i2cp->rxbytes == 0U) { - dp->CR1 &= ~I2C_CR1_RXIE; - } - } - } - } -#endif - - /* Partial transfer handling, restarting the transfer and returning.*/ - if ((isr & I2C_ISR_TCR) != 0U) { - if (i2cp->state == I2C_ACTIVE_TX) { - i2c_lld_setup_tx_transfer(i2cp); - } - else { - i2c_lld_setup_rx_transfer(i2cp); - } - return; - } - - /* The following condition is true if a transfer phase has been completed.*/ - if ((isr & I2C_ISR_TC) != 0U) { - if (i2cp->state == I2C_ACTIVE_TX) { - /* End of the transmit phase.*/ - -#if STM32_I2C_USE_DMA == TRUE - /* Disabling TX DMA channel.*/ - dmaStreamDisable(i2cp->dmatx); -#endif - - /* Starting receive phase if necessary.*/ - if (i2c_lld_get_rxbytes(i2cp) > 0U) { - /* Setting up the peripheral.*/ - i2c_lld_setup_rx_transfer(i2cp); - -#if STM32_I2C_USE_DMA == TRUE - /* Enabling RX DMA.*/ - dmaStreamEnable(i2cp->dmarx); -#else - /* RX interrupt enabled.*/ - dp->CR1 |= I2C_CR1_RXIE; -#endif - - /* Starts the read operation.*/ - dp->CR2 |= I2C_CR2_START; - - /* State change.*/ - i2cp->state = I2C_ACTIVE_RX; - - /* Note, returning because the transaction is not over yet.*/ - return; - } - } - else { - /* End of the receive phase.*/ -#if STM32_I2C_USE_DMA == TRUE - /* Disabling RX DMA channel.*/ - dmaStreamDisable(i2cp->dmarx); -#endif - } - - /* Transaction finished sending the STOP.*/ - dp->CR2 |= I2C_CR2_STOP; - - /* Make sure no more 'Transfer Complete' interrupts.*/ - dp->CR1 &= ~I2C_CR1_TCIE; - - /* Normal transaction end.*/ - _i2c_wakeup_isr(i2cp); - } -} - -/** - * @brief I2C error handler. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] isr content of the ISR register to be decoded - * - * @notapi - */ -static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint32_t isr) { - -#if STM32_I2C_USE_DMA == TRUE - /* Clears DMA interrupt flags just to be safe.*/ - dmaStreamDisable(i2cp->dmatx); - dmaStreamDisable(i2cp->dmarx); -#else - /* Disabling RX and TX interrupts.*/ - i2cp->i2c->CR1 &= ~(I2C_CR1_TXIE | I2C_CR1_RXIE); -#endif - - if (isr & I2C_ISR_BERR) - i2cp->errors |= I2C_BUS_ERROR; - - if (isr & I2C_ISR_ARLO) - i2cp->errors |= I2C_ARBITRATION_LOST; - - if (isr & I2C_ISR_OVR) - i2cp->errors |= I2C_OVERRUN; - - if (isr & I2C_ISR_TIMEOUT) - i2cp->errors |= I2C_TIMEOUT; - - /* If some error has been identified then sends wakes the waiting thread.*/ - if (i2cp->errors != I2C_NO_ERROR) - _i2c_wakeup_error_isr(i2cp); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_I2C_USE_I2C1 || defined(__DOXYGEN__) -#if defined(STM32_I2C1_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C1 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C1_GLOBAL_HANDLER) { - uint32_t isr = I2CD1.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD1.i2c->ICR = isr; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD1, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD1, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C1_EVENT_HANDLER) && defined(STM32_I2C1_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) { - uint32_t isr = I2CD1.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD1.i2c->ICR = isr & I2C_INT_MASK; - - i2c_lld_serve_interrupt(&I2CD1, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C1_ERROR_HANDLER) { - uint32_t isr = I2CD1.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD1.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD1, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C1 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 || defined(__DOXYGEN__) -#if defined(STM32_I2C2_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C2 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C2_GLOBAL_HANDLER) { - uint32_t isr = I2CD2.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD2.i2c->ICR = isr; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD2, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD2, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C2_EVENT_HANDLER) && defined(STM32_I2C2_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C2_EVENT_HANDLER) { - uint32_t isr = I2CD2.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD2.i2c->ICR = isr & I2C_INT_MASK; - - i2c_lld_serve_interrupt(&I2CD2, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C2_ERROR_HANDLER) { - uint32_t isr = I2CD2.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD2.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD2, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C2 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 || defined(__DOXYGEN__) -#if defined(STM32_I2C3_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C3 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C3_GLOBAL_HANDLER) { - uint32_t isr = I2CD3.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD3.i2c->ICR = isr; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD3, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C3_EVENT_HANDLER) && defined(STM32_I2C3_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C3_EVENT_HANDLER) { - uint32_t isr = I2CD3.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD3.i2c->ICR = isr & I2C_INT_MASK; - - i2c_lld_serve_interrupt(&I2CD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C3_ERROR_HANDLER) { - uint32_t isr = I2CD3.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD3.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD3, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C3 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C3 */ - -#if STM32_I2C_USE_I2C4 || defined(__DOXYGEN__) -#if defined(STM32_I2C4_GLOBAL_HANDLER) || defined(__DOXYGEN__) -/** - * @brief I2C4 event interrupt handler. - * - * @notapi - */ -OSAL_IRQ_HANDLER(STM32_I2C4_GLOBAL_HANDLER) { - uint32_t isr = I2CD4.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD4.i2c->ICR = isr; - - if (isr & I2C_ERROR_MASK) - i2c_lld_serve_error_interrupt(&I2CD4, isr); - else if (isr & I2C_INT_MASK) - i2c_lld_serve_interrupt(&I2CD4, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#elif defined(STM32_I2C4_EVENT_HANDLER) && defined(STM32_I2C4_ERROR_HANDLER) -OSAL_IRQ_HANDLER(STM32_I2C4_EVENT_HANDLER) { - uint32_t isr = I2CD4.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD4.i2c->ICR = isr & I2C_INT_MASK; - - i2c_lld_serve_interrupt(&I2CD4, isr); - - OSAL_IRQ_EPILOGUE(); -} - -OSAL_IRQ_HANDLER(STM32_I2C4_ERROR_HANDLER) { - uint32_t isr = I2CD4.i2c->ISR; - - OSAL_IRQ_PROLOGUE(); - - /* Clearing IRQ bits.*/ - I2CD4.i2c->ICR = isr & I2C_ERROR_MASK; - - i2c_lld_serve_error_interrupt(&I2CD4, isr); - - OSAL_IRQ_EPILOGUE(); -} - -#else -#error "I2C4 interrupt handlers not defined" -#endif -#endif /* STM32_I2C_USE_I2C4 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2C driver initialization. - * - * @notapi - */ -void i2c_lld_init(void) { - -#if STM32_I2C_USE_I2C1 - i2cObjectInit(&I2CD1); - I2CD1.thread = NULL; - I2CD1.i2c = I2C1; -#if STM32_I2C_USE_DMA == TRUE - I2CD1.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C1_RX_DMA_STREAM); - I2CD1.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C1_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - i2cObjectInit(&I2CD2); - I2CD2.thread = NULL; - I2CD2.i2c = I2C2; -#if STM32_I2C_USE_DMA == TRUE - I2CD2.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C2_RX_DMA_STREAM); - I2CD2.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C2_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - i2cObjectInit(&I2CD3); - I2CD3.thread = NULL; - I2CD3.i2c = I2C3; -#if STM32_I2C_USE_DMA == TRUE - I2CD3.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C3_RX_DMA_STREAM); - I2CD3.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C3_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C3 */ - -#if STM32_I2C_USE_I2C4 - i2cObjectInit(&I2CD4); - I2CD4.thread = NULL; - I2CD4.i2c = I2C4; -#if STM32_I2C_USE_DMA == TRUE - I2CD4.dmarx = STM32_DMA_STREAM(STM32_I2C_I2C4_RX_DMA_STREAM); - I2CD4.dmatx = STM32_DMA_STREAM(STM32_I2C_I2C4_TX_DMA_STREAM); -#endif -#endif /* STM32_I2C_USE_I2C4 */ -} - -/** - * @brief Configures and activates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_start(I2CDriver *i2cp) { - I2C_TypeDef *dp = i2cp->i2c; - -#if STM32_I2C_USE_DMA == TRUE - /* Common DMA modes.*/ - i2cp->txdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_M2P; - i2cp->rxdmamode = DMAMODE_COMMON | STM32_DMA_CR_DIR_P2M; -#endif - - /* Make sure I2C peripheral is disabled */ - dp->CR1 &= ~I2C_CR1_PE; - - /* If in stopped state then enables the I2C and DMA clocks.*/ - if (i2cp->state == I2C_STOP) { - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { - - rccResetI2C1(); - rccEnableI2C1(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C1_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C1_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C1_DMA_PRIORITY); - } -#endif /* STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C1_GLOBAL_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); -#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) - nvicEnableVector(STM32_I2C1_EVENT_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C1_ERROR_NUMBER, STM32_I2C_I2C1_IRQ_PRIORITY); -#else -#error "I2C1 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C1 */ - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { - - rccResetI2C2(); - rccEnableI2C2(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C2_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C2_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C2_DMA_PRIORITY); - } -#endif /*STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C2_GLOBAL_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); -#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) - nvicEnableVector(STM32_I2C2_EVENT_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C2_ERROR_NUMBER, STM32_I2C_I2C2_IRQ_PRIORITY); -#else -#error "I2C2 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C2 */ - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { - - rccResetI2C3(); - rccEnableI2C3(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C3_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C3_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C3_DMA_PRIORITY); - } -#endif /*STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C3_GLOBAL_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); -#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) - nvicEnableVector(STM32_I2C3_EVENT_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C3_ERROR_NUMBER, STM32_I2C_I2C3_IRQ_PRIORITY); -#else -#error "I2C3 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C3 */ - -#if STM32_I2C_USE_I2C4 - if (&I2CD4 == i2cp) { - - rccResetI2C4(); - rccEnableI2C4(FALSE); -#if STM32_I2C_USE_DMA == TRUE - { - bool b; - - b = dmaStreamAllocate(i2cp->dmarx, - STM32_I2C_I2C4_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(i2cp->dmatx, - STM32_I2C_I2C4_IRQ_PRIORITY, - NULL, - (void *)i2cp); - osalDbgAssert(!b, "stream already allocated"); - - i2cp->rxdmamode |= STM32_DMA_CR_CHSEL(I2C4_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); - i2cp->txdmamode |= STM32_DMA_CR_CHSEL(I2C4_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2C_I2C4_DMA_PRIORITY); - } -#endif /*STM32_I2C_USE_DMA == TRUE */ - -#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicEnableVector(STM32_I2C4_GLOBAL_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); -#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) - nvicEnableVector(STM32_I2C4_EVENT_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); - nvicEnableVector(STM32_I2C4_ERROR_NUMBER, STM32_I2C_I2C4_IRQ_PRIORITY); -#else -#error "I2C4 interrupt numbers not defined" -#endif - } -#endif /* STM32_I2C_USE_I2C4 */ - } - -#if STM32_I2C_USE_DMA == TRUE - /* I2C registers pointed by the DMA.*/ - dmaStreamSetPeripheral(i2cp->dmarx, &dp->RXDR); - dmaStreamSetPeripheral(i2cp->dmatx, &dp->TXDR); -#endif - - /* Reset i2c peripheral, the TCIE bit will be handled separately.*/ - dp->CR1 = i2cp->config->cr1 | -#if STM32_I2C_USE_DMA == TRUE - I2C_CR1_TXDMAEN | I2C_CR1_RXDMAEN | /* Enable only if using DMA */ -#endif - I2C_CR1_ERRIE | I2C_CR1_NACKIE; - - /* Setup I2C parameters.*/ - dp->TIMINGR = i2cp->config->timingr; - - /* Ready to go.*/ - dp->CR1 |= I2C_CR1_PE; -} - -/** - * @brief Deactivates the I2C peripheral. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -void i2c_lld_stop(I2CDriver *i2cp) { - - /* If not in stopped state then disables the I2C clock.*/ - if (i2cp->state != I2C_STOP) { - - /* I2C disable.*/ - i2c_lld_abort_operation(i2cp); -#if STM32_I2C_USE_DMA == TRUE - dmaStreamRelease(i2cp->dmatx); - dmaStreamRelease(i2cp->dmarx); -#endif - -#if STM32_I2C_USE_I2C1 - if (&I2CD1 == i2cp) { -#if defined(STM32_I2C1_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C1_GLOBAL_NUMBER); -#elif defined(STM32_I2C1_EVENT_NUMBER) && defined(STM32_I2C1_ERROR_NUMBER) - nvicDisableVector(STM32_I2C1_EVENT_NUMBER); - nvicDisableVector(STM32_I2C1_ERROR_NUMBER); -#else -#error "I2C1 interrupt numbers not defined" -#endif - - rccDisableI2C1(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C2 - if (&I2CD2 == i2cp) { -#if defined(STM32_I2C2_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C2_GLOBAL_NUMBER); -#elif defined(STM32_I2C2_EVENT_NUMBER) && defined(STM32_I2C2_ERROR_NUMBER) - nvicDisableVector(STM32_I2C2_EVENT_NUMBER); - nvicDisableVector(STM32_I2C2_ERROR_NUMBER); -#else -#error "I2C2 interrupt numbers not defined" -#endif - - rccDisableI2C2(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C3 - if (&I2CD3 == i2cp) { -#if defined(STM32_I2C3_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C3_GLOBAL_NUMBER); -#elif defined(STM32_I2C3_EVENT_NUMBER) && defined(STM32_I2C3_ERROR_NUMBER) - nvicDisableVector(STM32_I2C3_EVENT_NUMBER); - nvicDisableVector(STM32_I2C3_ERROR_NUMBER); -#else -#error "I2C3 interrupt numbers not defined" -#endif - - rccDisableI2C3(FALSE); - } -#endif - -#if STM32_I2C_USE_I2C4 - if (&I2CD4 == i2cp) { -#if defined(STM32_I2C4_GLOBAL_NUMBER) || defined(__DOXYGEN__) - nvicDisableVector(STM32_I2C4_GLOBAL_NUMBER); -#elif defined(STM32_I2C4_EVENT_NUMBER) && defined(STM32_I2C4_ERROR_NUMBER) - nvicDisableVector(STM32_I2C4_EVENT_NUMBER); - nvicDisableVector(STM32_I2C4_ERROR_NUMBER); -#else -#error "I2C4 interrupt numbers not defined" -#endif - - rccDisableI2C4(FALSE); - } -#endif - } -} - -/** - * @brief Receives data via the I2C bus as master. - * @details Number of receiving bytes must be more than 1 on STM32F1x. This is - * hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - msg_t msg; - I2C_TypeDef *dp = i2cp->i2c; - systime_t start, end; - - /* Resetting error flags for this transfer.*/ - i2cp->errors = I2C_NO_ERROR; - - /* Releases the lock from high level driver.*/ - osalSysUnlock(); - -#if STM32_I2C_USE_DMA == TRUE - /* RX DMA setup.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); -#else - i2cp->rxptr = rxbuf; - i2cp->rxbytes = rxbytes; -#endif - - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); - end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); - - /* Waits until BUSY flag is reset or, alternatively, for a timeout - condition.*/ - while (true) { - osalSysLock(); - - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ - if ((dp->ISR & I2C_ISR_BUSY) == 0) - break; - - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { - return MSG_TIMEOUT; - } - - osalSysUnlock(); - } - - /* Setting up the slave address.*/ - i2c_lld_set_address(i2cp, addr); - - /* Setting up the peripheral.*/ - i2c_lld_setup_rx_transfer(i2cp); - -#if STM32_I2C_USE_DMA == TRUE - /* Enabling RX DMA.*/ - dmaStreamEnable(i2cp->dmarx); - - /* Transfer complete interrupt enabled.*/ - dp->CR1 |= I2C_CR1_TCIE; -#else - - /* Transfer complete and RX interrupts enabled.*/ - dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_RXIE; -#endif - - /* Starts the operation.*/ - dp->CR2 |= I2C_CR2_START; - - /* Waits for the operation completion or a timeout.*/ - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - - /* In case of a software timeout a STOP is sent as an extreme attempt - to release the bus.*/ - if (msg == MSG_TIMEOUT) { - dp->CR2 |= I2C_CR2_STOP; - } - - return msg; -} - -/** - * @brief Transmits data via the I2C bus as master. - * @details Number of receiving bytes must be 0 or more than 1 on STM32F1x. - * This is hardware restriction. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * @param[in] addr slave device address - * @param[in] txbuf pointer to the transmit buffer - * @param[in] txbytes number of bytes to be transmitted - * @param[out] rxbuf pointer to the receive buffer - * @param[in] rxbytes number of bytes to be received - * @param[in] timeout the number of ticks before the operation timeouts, - * the following special values are allowed: - * - @a TIME_INFINITE no timeout. - * . - * @return The operation status. - * @retval MSG_OK if the function succeeded. - * @retval MSG_RESET if one or more I2C errors occurred, the errors can - * be retrieved using @p i2cGetErrors(). - * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a - * timeout the driver must be stopped and restarted - * because the bus is in an uncertain state. - * - * @notapi - */ -msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout) { - msg_t msg; - I2C_TypeDef *dp = i2cp->i2c; - systime_t start, end; - - /* Resetting error flags for this transfer.*/ - i2cp->errors = I2C_NO_ERROR; - - /* Releases the lock from high level driver.*/ - osalSysUnlock(); - -#if STM32_I2C_USE_DMA == TRUE - /* TX DMA setup.*/ - dmaStreamSetMode(i2cp->dmatx, i2cp->txdmamode); - dmaStreamSetMemory0(i2cp->dmatx, txbuf); - dmaStreamSetTransactionSize(i2cp->dmatx, txbytes); - - /* RX DMA setup, note, rxbytes can be zero but we write the value anyway.*/ - dmaStreamSetMode(i2cp->dmarx, i2cp->rxdmamode); - dmaStreamSetMemory0(i2cp->dmarx, rxbuf); - dmaStreamSetTransactionSize(i2cp->dmarx, rxbytes); -#else - i2cp->txptr = txbuf; - i2cp->txbytes = txbytes; - i2cp->rxptr = rxbuf; - i2cp->rxbytes = rxbytes; -#endif - - /* Calculating the time window for the timeout on the busy bus condition.*/ - start = osalOsGetSystemTimeX(); - end = start + OSAL_MS2ST(STM32_I2C_BUSY_TIMEOUT); - - /* Waits until BUSY flag is reset or, alternatively, for a timeout - condition.*/ - while (true) { - osalSysLock(); - - /* If the bus is not busy then the operation can continue, note, the - loop is exited in the locked state.*/ - if ((dp->ISR & I2C_ISR_BUSY) == 0) - break; - - /* If the system time went outside the allowed window then a timeout - condition is returned.*/ - if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) { - return MSG_TIMEOUT; - } - - osalSysUnlock(); - } - - /* Setting up the slave address.*/ - i2c_lld_set_address(i2cp, addr); - - /* Preparing the transfer.*/ - i2c_lld_setup_tx_transfer(i2cp); - -#if STM32_I2C_USE_DMA == TRUE - /* Enabling TX DMA.*/ - dmaStreamEnable(i2cp->dmatx); - - /* Transfer complete interrupt enabled.*/ - dp->CR1 |= I2C_CR1_TCIE; -#else - /* Transfer complete and TX interrupts enabled.*/ - dp->CR1 |= I2C_CR1_TCIE | I2C_CR1_TXIE; -#endif - - /* Starts the operation.*/ - dp->CR2 |= I2C_CR2_START; - - /* Waits for the operation completion or a timeout.*/ - msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout); - - /* In case of a software timeout a STOP is sent as an extreme attempt - to release the bus.*/ - if (msg == MSG_TIMEOUT) { - dp->CR2 |= I2C_CR2_STOP; - } - - return msg; -} - -#endif /* HAL_USE_I2C */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h b/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h deleted file mode 100644 index eede83103..000000000 --- a/os/hal/ports/STM32/LLD/I2Cv2/i2c_lld.h +++ /dev/null @@ -1,502 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/I2Cv2/i2c_lld.h - * @brief STM32 I2C subsystem low level driver header. - * - * @addtogroup I2C - * @{ - */ - -#ifndef _I2C_LLD_H_ -#define _I2C_LLD_H_ - -#if HAL_USE_I2C || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name TIMINGR register definitions - * @{ - */ -#define STM32_TIMINGR_PRESC_MASK (15U << 28) -#define STM32_TIMINGR_PRESC(n) ((n) << 28) -#define STM32_TIMINGR_SCLDEL_MASK (15U << 20) -#define STM32_TIMINGR_SCLDEL(n) ((n) << 20) -#define STM32_TIMINGR_SDADEL_MASK (15U << 16) -#define STM32_TIMINGR_SDADEL(n) ((n) << 16) -#define STM32_TIMINGR_SCLH_MASK (255U << 8) -#define STM32_TIMINGR_SCLH(n) ((n) << 8) -#define STM32_TIMINGR_SCLL_MASK (255U << 0) -#define STM32_TIMINGR_SCLL(n) ((n) << 0) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2C1 driver enable switch. - * @details If set to @p TRUE the support for I2C1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C1) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C1 FALSE -#endif - -/** - * @brief I2C2 driver enable switch. - * @details If set to @p TRUE the support for I2C2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C2) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C2 FALSE -#endif - -/** - * @brief I2C3 driver enable switch. - * @details If set to @p TRUE the support for I2C3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C3) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C3 FALSE -#endif - -/** - * @brief I2C4 driver enable switch. - * @details If set to @p TRUE the support for I2C4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_I2C_USE_I2C4) || defined(__DOXYGEN__) -#define STM32_I2C_USE_I2C4 FALSE -#endif - -/** - * @brief I2C timeout on busy condition in milliseconds. - */ -#if !defined(STM32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_I2C_BUSY_TIMEOUT 50 -#endif - -/** - * @brief I2C1 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C2 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C3 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2C4 interrupt priority level setting. - */ -#if !defined(STM32_I2C_I2C4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C4_IRQ_PRIORITY 10 -#endif - -/** - * @brief DMA use switch. - */ -#if !defined(STM32_I2C_USE_DMA) || defined(__DOXYGEN__) -#define STM32_I2C_USE_DMA TRUE -#endif - -/** - * @brief I2C1 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C1_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C2 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C2_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C3 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C3_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C4 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_I2C_I2C4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2C_I2C4_DMA_PRIORITY 1 -#endif - -/** - * @brief I2C DMA error hook. - * @note The default action for DMA errors is a system halt because DMA - * error can only happen because programming errors. - */ -#if !defined(STM32_I2C_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/** @brief error checks */ -#if STM32_I2C_USE_I2C1 && !STM32_HAS_I2C1 -#error "I2C1 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C2 && !STM32_HAS_I2C2 -#error "I2C2 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C3 && !STM32_HAS_I2C3 -#error "I2C3 not present in the selected device" -#endif - -#if STM32_I2C_USE_I2C4 && !STM32_HAS_I2C4 -#error "I2C4 not present in the selected device" -#endif - -#if !STM32_I2C_USE_I2C1 && !STM32_I2C_USE_I2C2 && !STM32_I2C_USE_I2C3 && \ - !STM32_I2C_USE_I2C4 -#error "I2C driver activated but no I2C peripheral assigned" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C3" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2C_I2C4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to I2C4" -#endif - -#if STM32_I2C_USE_DMA == TRUE -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C1" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C2" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C3" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2C_I2C4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to I2C4" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_I2C_USE_I2C1 && (!defined(STM32_I2C_I2C1_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C1_TX_DMA_STREAM)) -#error "I2C1 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C2 && (!defined(STM32_I2C_I2C2_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C2_TX_DMA_STREAM)) -#error "I2C2 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C3 && (!defined(STM32_I2C_I2C3_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C3_TX_DMA_STREAM)) -#error "I2C3 DMA streams not defined" -#endif - -#if STM32_I2C_USE_I2C4 && (!defined(STM32_I2C_I2C4_RX_DMA_STREAM) || \ - !defined(STM32_I2C_I2C4_TX_DMA_STREAM)) -#error "I2C4 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_RX_DMA_STREAM, \ - STM32_I2C1_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 RX" -#endif - -#if STM32_I2C_USE_I2C1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C1_TX_DMA_STREAM, \ - STM32_I2C1_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C1 TX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_RX_DMA_STREAM, \ - STM32_I2C2_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 RX" -#endif - -#if STM32_I2C_USE_I2C2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C2_TX_DMA_STREAM, \ - STM32_I2C2_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C2 TX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_RX_DMA_STREAM, \ - STM32_I2C3_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 RX" -#endif - -#if STM32_I2C_USE_I2C3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C3_TX_DMA_STREAM, \ - STM32_I2C3_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C3 TX" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_RX_DMA_STREAM, \ - STM32_I2C4_RX_DMA_MSK) -#error "invalid DMA stream associated to I2C4 RX" -#endif - -#if STM32_I2C_USE_I2C4 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2C_I2C4_TX_DMA_STREAM, \ - STM32_I2C4_TX_DMA_MSK) -#error "invalid DMA stream associated to I2C4 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif -#endif /* STM32_I2C_USE_DMA == TRUE */ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type representing an I2C address. - */ -typedef uint16_t i2caddr_t; - -/** - * @brief Type of I2C driver condition flags. - */ -typedef uint32_t i2cflags_t; - -/** - * @brief Type of I2C driver configuration structure. - */ -typedef struct { - /** - * @brief TIMINGR register initialization. - * @note Refer to the STM32 reference manual, the values are affected - * by the system clock settings in mcuconf.h. - */ - uint32_t timingr; - /** - * @brief CR1 register initialization. - * @note Leave to zero unless you know what you are doing. - */ - uint32_t cr1; - /** - * @brief CR2 register initialization. - * @note Only the ADD10 bit can eventually be specified here. - */ - uint32_t cr2; -} I2CConfig; - -/** - * @brief Type of a structure representing an I2C driver. - */ -typedef struct I2CDriver I2CDriver; - -/** - * @brief Structure representing an I2C driver. - */ -struct I2CDriver { - /** - * @brief Driver state. - */ - i2cstate_t state; - /** - * @brief Current configuration data. - */ - const I2CConfig *config; - /** - * @brief Error flags. - */ - i2cflags_t errors; -#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - mutex_t mutex; -#endif /* I2C_USE_MUTUAL_EXCLUSION */ -#if defined(I2C_DRIVER_EXT_FIELDS) - I2C_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Thread waiting for I/O completion. - */ - thread_reference_t thread; -#if (STM32_I2C_USE_DMA == TRUE) || defined(__DOXYGEN__) - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; - /** - * @brief Receive DMA channel. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dmatx; -#else /* STM32_I2C_USE_DMA == FALSE */ - /** - * @brief Pointer to the next TX buffer location. - */ - const uint8_t *txptr; - /** - * @brief Number of bytes in TX phase. - */ - size_t txbytes; - /** - * @brief Pointer to the next RX buffer location. - */ - uint8_t *rxptr; - /** - * @brief Number of bytes in RX phase. - */ - size_t rxbytes; -#endif /* STM32_I2C_USE_DMA == FALSE */ - /** - * @brief Pointer to the I2Cx registers block. - */ - I2C_TypeDef *i2c; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Get errors from I2C driver. - * - * @param[in] i2cp pointer to the @p I2CDriver object - * - * @notapi - */ -#define i2c_lld_get_errors(i2cp) ((i2cp)->errors) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -#if STM32_I2C_USE_I2C1 -extern I2CDriver I2CD1; -#endif - -#if STM32_I2C_USE_I2C2 -extern I2CDriver I2CD2; -#endif - -#if STM32_I2C_USE_I2C3 -extern I2CDriver I2CD3; -#endif - -#if STM32_I2C_USE_I2C4 -extern I2CDriver I2CD4; -#endif - -#endif /* !defined(__DOXYGEN__) */ - -#ifdef __cplusplus -extern "C" { -#endif - void i2c_lld_init(void); - void i2c_lld_start(I2CDriver *i2cp); - void i2c_lld_stop(I2CDriver *i2cp); - msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr, - const uint8_t *txbuf, size_t txbytes, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); - msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr, - uint8_t *rxbuf, size_t rxbytes, - systime_t timeout); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2C */ - -#endif /* _I2C_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.c b/os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.c new file mode 100644 index 000000000..a223c00d5 --- /dev/null +++ b/os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.c @@ -0,0 +1,740 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/mac_lld.c + * @brief STM32 low level MAC driver code. + * + * @addtogroup MAC + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_MAC || defined(__DOXYGEN__) + +#include "mii.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define BUFFER_SIZE ((((STM32_MAC_BUFFERS_SIZE - 1) | 3) + 1) / 4) + +/* MII divider optimal value.*/ +#if (STM32_HCLK >= 150000000) +#define MACMIIDR_CR ETH_MACMIIAR_CR_Div102 +#elif (STM32_HCLK >= 100000000) +#define MACMIIDR_CR ETH_MACMIIAR_CR_Div62 +#elif (STM32_HCLK >= 60000000) +#define MACMIIDR_CR ETH_MACMIIAR_CR_Div42 +#elif (STM32_HCLK >= 35000000) +#define MACMIIDR_CR ETH_MACMIIAR_CR_Div26 +#elif (STM32_HCLK >= 20000000) +#define MACMIIDR_CR ETH_MACMIIAR_CR_Div16 +#else +#error "STM32_HCLK below minimum frequency for ETH operations (20MHz)" +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief Ethernet driver 1. + */ +MACDriver ETHD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const uint8_t default_mac_address[] = {0xAA, 0x55, 0x13, + 0x37, 0x01, 0x10}; + +static stm32_eth_rx_descriptor_t __eth_rd[STM32_MAC_RECEIVE_BUFFERS]; +static stm32_eth_tx_descriptor_t __eth_td[STM32_MAC_TRANSMIT_BUFFERS]; + +static uint32_t __eth_rb[STM32_MAC_RECEIVE_BUFFERS][BUFFER_SIZE]; +static uint32_t __eth_tb[STM32_MAC_TRANSMIT_BUFFERS][BUFFER_SIZE]; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Writes a PHY register. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[in] reg register number + * @param[in] value new register value + * + * @notapi + */ +void mii_write(MACDriver *macp, uint32_t reg, uint32_t value) { + + ETH->MACMIIDR = value; + ETH->MACMIIAR = macp->phyaddr | (reg << 6) | MACMIIDR_CR | + ETH_MACMIIAR_MW | ETH_MACMIIAR_MB; + while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) != 0) + ; +} + +/** + * @brief Reads a PHY register. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[in] reg register number + * + * @return The PHY register content. + * + * @notapi + */ +uint32_t mii_read(MACDriver *macp, uint32_t reg) { + + ETH->MACMIIAR = macp->phyaddr | (reg << 6) | MACMIIDR_CR | ETH_MACMIIAR_MB; + while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) != 0) + ; + return ETH->MACMIIDR; +} + +#if !defined(BOARD_PHY_ADDRESS) +/** + * @brief PHY address detection. + * + * @param[in] macp pointer to the @p MACDriver object + */ +static void mii_find_phy(MACDriver *macp) { + uint32_t i; + +#if STM32_MAC_PHY_TIMEOUT > 0 + unsigned n = STM32_MAC_PHY_TIMEOUT; + do { +#endif + for (i = 0U; i < 31U; i++) { + macp->phyaddr = i << 11U; + ETH->MACMIIDR = (i << 6U) | MACMIIDR_CR; + if ((mii_read(macp, MII_PHYSID1) == (BOARD_PHY_ID >> 16U)) && + ((mii_read(macp, MII_PHYSID2) & 0xFFF0U) == (BOARD_PHY_ID & 0xFFF0U))) { + return; + } + } +#if STM32_MAC_PHY_TIMEOUT > 0 + n--; + } while (n > 0U); +#endif + /* Wrong or defective board.*/ + osalSysHalt("MAC failure"); +} +#endif + +/** + * @brief MAC address setup. + * + * @param[in] p pointer to a six bytes buffer containing the MAC + * address + */ +static void mac_lld_set_address(const uint8_t *p) { + + /* MAC address configuration, only a single address comparator is used, + hash table not used.*/ + ETH->MACA0HR = ((uint32_t)p[5] << 8) | + ((uint32_t)p[4] << 0); + ETH->MACA0LR = ((uint32_t)p[3] << 24) | + ((uint32_t)p[2] << 16) | + ((uint32_t)p[1] << 8) | + ((uint32_t)p[0] << 0); + ETH->MACA1HR = 0x0000FFFF; + ETH->MACA1LR = 0xFFFFFFFF; + ETH->MACA2HR = 0x0000FFFF; + ETH->MACA2LR = 0xFFFFFFFF; + ETH->MACA3HR = 0x0000FFFF; + ETH->MACA3LR = 0xFFFFFFFF; + ETH->MACHTHR = 0; + ETH->MACHTLR = 0; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +OSAL_IRQ_HANDLER(STM32_ETH_HANDLER) { + uint32_t dmasr; + + OSAL_IRQ_PROLOGUE(); + + dmasr = ETH->DMASR; + ETH->DMASR = dmasr; /* Clear status bits.*/ + + if (dmasr & ETH_DMASR_RS) { + /* Data Received.*/ + osalSysLockFromISR(); + osalThreadDequeueAllI(ÐD1.rdqueue, MSG_RESET); +#if MAC_USE_EVENTS + osalEventBroadcastFlagsI(ÐD1.rdevent, 0); +#endif + osalSysUnlockFromISR(); + } + + if (dmasr & ETH_DMASR_TS) { + /* Data Transmitted.*/ + osalSysLockFromISR(); + osalThreadDequeueAllI(ÐD1.tdqueue, MSG_RESET); + osalSysUnlockFromISR(); + } + + OSAL_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level MAC initialization. + * + * @notapi + */ +void mac_lld_init(void) { + unsigned i; + + macObjectInit(ÐD1); + ETHD1.link_up = false; + + /* Descriptor tables are initialized in chained mode, note that the first + word is not initialized here but in mac_lld_start().*/ + for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) { + __eth_rd[i].rdes1 = STM32_RDES1_RCH | STM32_MAC_BUFFERS_SIZE; + __eth_rd[i].rdes2 = (uint32_t)__eth_rb[i]; + __eth_rd[i].rdes3 = (uint32_t)&__eth_rd[(i + 1) % STM32_MAC_RECEIVE_BUFFERS]; + } + for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) { + __eth_td[i].tdes1 = 0; + __eth_td[i].tdes2 = (uint32_t)__eth_tb[i]; + __eth_td[i].tdes3 = (uint32_t)&__eth_td[(i + 1) % STM32_MAC_TRANSMIT_BUFFERS]; + } + + /* Selection of the RMII or MII mode based on info exported by board.h.*/ +#if defined(STM32F10X_CL) +#if defined(BOARD_PHY_RMII) + AFIO->MAPR |= AFIO_MAPR_MII_RMII_SEL; +#else + AFIO->MAPR &= ~AFIO_MAPR_MII_RMII_SEL; +#endif +#elif defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) +#if defined(BOARD_PHY_RMII) + SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; +#else + SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL; +#endif +#else +#error "unsupported STM32 platform for MAC driver" +#endif + + /* Reset of the MAC core.*/ + rccResetETH(); + + /* MAC clocks temporary activation.*/ + rccEnableETH(false); + + /* PHY address setup.*/ +#if defined(BOARD_PHY_ADDRESS) + ETHD1.phyaddr = BOARD_PHY_ADDRESS << 11; +#else + mii_find_phy(ÐD1); +#endif + +#if defined(BOARD_PHY_RESET) + /* PHY board-specific reset procedure.*/ + BOARD_PHY_RESET(); +#else + /* PHY soft reset procedure.*/ + mii_write(ÐD1, MII_BMCR, BMCR_RESET); +#if defined(BOARD_PHY_RESET_DELAY) + osalSysPolledDelayX(BOARD_PHY_RESET_DELAY); +#endif + while (mii_read(ÐD1, MII_BMCR) & BMCR_RESET) + ; +#endif + +#if STM32_MAC_ETH1_CHANGE_PHY_STATE + /* PHY in power down mode until the driver will be started.*/ + mii_write(ÐD1, MII_BMCR, mii_read(ÐD1, MII_BMCR) | BMCR_PDOWN); +#endif + + /* MAC clocks stopped again.*/ + rccDisableETH(false); +} + +/** + * @brief Configures and activates the MAC peripheral. + * + * @param[in] macp pointer to the @p MACDriver object + * + * @notapi + */ +void mac_lld_start(MACDriver *macp) { + unsigned i; + + /* Resets the state of all descriptors.*/ + for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) + __eth_rd[i].rdes0 = STM32_RDES0_OWN; + macp->rxptr = (stm32_eth_rx_descriptor_t *)__eth_rd; + for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) + __eth_td[i].tdes0 = STM32_TDES0_TCH; + macp->txptr = (stm32_eth_tx_descriptor_t *)__eth_td; + + /* MAC clocks activation and commanded reset procedure.*/ + rccEnableETH(false); +#if defined(STM32_MAC_DMABMR_SR) + ETH->DMABMR |= ETH_DMABMR_SR; + while(ETH->DMABMR & ETH_DMABMR_SR) + ; +#endif + + /* ISR vector enabled.*/ + nvicEnableVector(STM32_ETH_NUMBER, STM32_MAC_ETH1_IRQ_PRIORITY); + +#if STM32_MAC_ETH1_CHANGE_PHY_STATE + /* PHY in power up mode.*/ + mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) & ~BMCR_PDOWN); +#endif + + /* MAC configuration.*/ + ETH->MACFFR = 0; + ETH->MACFCR = 0; + ETH->MACVLANTR = 0; + + /* MAC address setup.*/ + if (macp->config->mac_address == NULL) + mac_lld_set_address(default_mac_address); + else + mac_lld_set_address(macp->config->mac_address); + + /* Transmitter and receiver enabled. + Note that the complete setup of the MAC is performed when the link + status is detected.*/ +#if STM32_MAC_IP_CHECKSUM_OFFLOAD + ETH->MACCR = ETH_MACCR_IPCO | ETH_MACCR_RE | ETH_MACCR_TE; +#else + ETH->MACCR = ETH_MACCR_RE | ETH_MACCR_TE; +#endif + + /* DMA configuration: + Descriptor chains pointers.*/ + ETH->DMARDLAR = (uint32_t)__eth_rd; + ETH->DMATDLAR = (uint32_t)__eth_td; + + /* Enabling required interrupt sources.*/ + ETH->DMASR = ETH->DMASR; + ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE; + + /* DMA general settings.*/ + ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_RDP_1Beat | ETH_DMABMR_PBL_1Beat; + + /* Transmit FIFO flush.*/ + ETH->DMAOMR = ETH_DMAOMR_FTF; + while (ETH->DMAOMR & ETH_DMAOMR_FTF) + ; + + /* DMA final configuration and start.*/ + ETH->DMAOMR = ETH_DMAOMR_DTCEFD | ETH_DMAOMR_RSF | ETH_DMAOMR_TSF | + ETH_DMAOMR_ST | ETH_DMAOMR_SR; +} + +/** + * @brief Deactivates the MAC peripheral. + * + * @param[in] macp pointer to the @p MACDriver object + * + * @notapi + */ +void mac_lld_stop(MACDriver *macp) { + + if (macp->state != MAC_STOP) { +#if STM32_MAC_ETH1_CHANGE_PHY_STATE + /* PHY in power down mode until the driver will be restarted.*/ + mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) | BMCR_PDOWN); +#endif + + /* MAC and DMA stopped.*/ + ETH->MACCR = 0; + ETH->DMAOMR = 0; + ETH->DMAIER = 0; + ETH->DMASR = ETH->DMASR; + + /* MAC clocks stopped.*/ + rccDisableETH(false); + + /* ISR vector disabled.*/ + nvicDisableVector(STM32_ETH_NUMBER); + } +} + +/** + * @brief Returns a transmission descriptor. + * @details One of the available transmission descriptors is locked and + * returned. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[out] tdp pointer to a @p MACTransmitDescriptor structure + * @return The operation status. + * @retval MSG_OK the descriptor has been obtained. + * @retval MSG_TIMEOUT descriptor not available. + * + * @notapi + */ +msg_t mac_lld_get_transmit_descriptor(MACDriver *macp, + MACTransmitDescriptor *tdp) { + stm32_eth_tx_descriptor_t *tdes; + + if (!macp->link_up) + return MSG_TIMEOUT; + + osalSysLock(); + + /* Get Current TX descriptor.*/ + tdes = macp->txptr; + + /* Ensure that descriptor isn't owned by the Ethernet DMA or locked by + another thread.*/ + if (tdes->tdes0 & (STM32_TDES0_OWN | STM32_TDES0_LOCKED)) { + osalSysUnlock(); + return MSG_TIMEOUT; + } + + /* Marks the current descriptor as locked using a reserved bit.*/ + tdes->tdes0 |= STM32_TDES0_LOCKED; + + /* Next TX descriptor to use.*/ + macp->txptr = (stm32_eth_tx_descriptor_t *)tdes->tdes3; + + osalSysUnlock(); + + /* Set the buffer size and configuration.*/ + tdp->offset = 0; + tdp->size = STM32_MAC_BUFFERS_SIZE; + tdp->physdesc = tdes; + + return MSG_OK; +} + +/** + * @brief Releases a transmit descriptor and starts the transmission of the + * enqueued data as a single frame. + * + * @param[in] tdp the pointer to the @p MACTransmitDescriptor structure + * + * @notapi + */ +void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp) { + + osalDbgAssert(!(tdp->physdesc->tdes0 & STM32_TDES0_OWN), + "attempt to release descriptor already owned by DMA"); + + osalSysLock(); + + /* Unlocks the descriptor and returns it to the DMA engine.*/ + tdp->physdesc->tdes1 = tdp->offset; + tdp->physdesc->tdes0 = STM32_TDES0_CIC(STM32_MAC_IP_CHECKSUM_OFFLOAD) | + STM32_TDES0_IC | STM32_TDES0_LS | STM32_TDES0_FS | + STM32_TDES0_TCH | STM32_TDES0_OWN; + + /* If the DMA engine is stalled then a restart request is issued.*/ + if ((ETH->DMASR & ETH_DMASR_TPS) == ETH_DMASR_TPS_Suspended) { + ETH->DMASR = ETH_DMASR_TBUS; + ETH->DMATPDR = ETH_DMASR_TBUS; /* Any value is OK.*/ + } + + osalSysUnlock(); +} + +/** + * @brief Returns a receive descriptor. + * + * @param[in] macp pointer to the @p MACDriver object + * @param[out] rdp pointer to a @p MACReceiveDescriptor structure + * @return The operation status. + * @retval MSG_OK the descriptor has been obtained. + * @retval MSG_TIMEOUT descriptor not available. + * + * @notapi + */ +msg_t mac_lld_get_receive_descriptor(MACDriver *macp, + MACReceiveDescriptor *rdp) { + stm32_eth_rx_descriptor_t *rdes; + + osalSysLock(); + + /* Get Current RX descriptor.*/ + rdes = macp->rxptr; + + /* Iterates through received frames until a valid one is found, invalid + frames are discarded.*/ + while (!(rdes->rdes0 & STM32_RDES0_OWN)) { + if (!(rdes->rdes0 & (STM32_RDES0_AFM | STM32_RDES0_ES)) +#if STM32_MAC_IP_CHECKSUM_OFFLOAD + && (rdes->rdes0 & STM32_RDES0_FT) + && !(rdes->rdes0 & (STM32_RDES0_IPHCE | STM32_RDES0_PCE)) +#endif + && (rdes->rdes0 & STM32_RDES0_FS) && (rdes->rdes0 & STM32_RDES0_LS)) { + /* Found a valid one.*/ + rdp->offset = 0; + rdp->size = ((rdes->rdes0 & STM32_RDES0_FL_MASK) >> 16) - 4; + rdp->physdesc = rdes; + macp->rxptr = (stm32_eth_rx_descriptor_t *)rdes->rdes3; + + osalSysUnlock(); + return MSG_OK; + } + /* Invalid frame found, purging.*/ + rdes->rdes0 = STM32_RDES0_OWN; + rdes = (stm32_eth_rx_descriptor_t *)rdes->rdes3; + } + + /* Next descriptor to check.*/ + macp->rxptr = rdes; + + osalSysUnlock(); + return MSG_TIMEOUT; +} + +/** + * @brief Releases a receive descriptor. + * @details The descriptor and its buffer are made available for more incoming + * frames. + * + * @param[in] rdp the pointer to the @p MACReceiveDescriptor structure + * + * @notapi + */ +void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp) { + + osalDbgAssert(!(rdp->physdesc->rdes0 & STM32_RDES0_OWN), + "attempt to release descriptor already owned by DMA"); + + osalSysLock(); + + /* Give buffer back to the Ethernet DMA.*/ + rdp->physdesc->rdes0 = STM32_RDES0_OWN; + + /* If the DMA engine is stalled then a restart request is issued.*/ + if ((ETH->DMASR & ETH_DMASR_RPS) == ETH_DMASR_RPS_Suspended) { + ETH->DMASR = ETH_DMASR_RBUS; + ETH->DMARPDR = ETH_DMASR_RBUS; /* Any value is OK.*/ + } + + osalSysUnlock(); +} + +/** + * @brief Updates and returns the link status. + * + * @param[in] macp pointer to the @p MACDriver object + * @return The link status. + * @retval true if the link is active. + * @retval false if the link is down. + * + * @notapi + */ +bool mac_lld_poll_link_status(MACDriver *macp) { + uint32_t maccr, bmsr, bmcr; + + maccr = ETH->MACCR; + + /* PHY CR and SR registers read.*/ + (void)mii_read(macp, MII_BMSR); + bmsr = mii_read(macp, MII_BMSR); + bmcr = mii_read(macp, MII_BMCR); + + /* Check on auto-negotiation mode.*/ + if (bmcr & BMCR_ANENABLE) { + uint32_t lpa; + + /* Auto-negotiation must be finished without faults and link established.*/ + if ((bmsr & (BMSR_LSTATUS | BMSR_RFAULT | BMSR_ANEGCOMPLETE)) != + (BMSR_LSTATUS | BMSR_ANEGCOMPLETE)) + return macp->link_up = false; + + /* Auto-negotiation enabled, checks the LPA register.*/ + lpa = mii_read(macp, MII_LPA); + + /* Check on link speed.*/ + if (lpa & (LPA_100HALF | LPA_100FULL | LPA_100BASE4)) + maccr |= ETH_MACCR_FES; + else + maccr &= ~ETH_MACCR_FES; + + /* Check on link mode.*/ + if (lpa & (LPA_10FULL | LPA_100FULL)) + maccr |= ETH_MACCR_DM; + else + maccr &= ~ETH_MACCR_DM; + } + else { + /* Link must be established.*/ + if (!(bmsr & BMSR_LSTATUS)) + return macp->link_up = false; + + /* Check on link speed.*/ + if (bmcr & BMCR_SPEED100) + maccr |= ETH_MACCR_FES; + else + maccr &= ~ETH_MACCR_FES; + + /* Check on link mode.*/ + if (bmcr & BMCR_FULLDPLX) + maccr |= ETH_MACCR_DM; + else + maccr &= ~ETH_MACCR_DM; + } + + /* Changes the mode in the MAC.*/ + ETH->MACCR = maccr; + + /* Returns the link status.*/ + return macp->link_up = true; +} + +/** + * @brief Writes to a transmit descriptor's stream. + * + * @param[in] tdp pointer to a @p MACTransmitDescriptor structure + * @param[in] buf pointer to the buffer containing the data to be + * written + * @param[in] size number of bytes to be written + * @return The number of bytes written into the descriptor's + * stream, this value can be less than the amount + * specified in the parameter @p size if the maximum + * frame size is reached. + * + * @notapi + */ +size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp, + uint8_t *buf, + size_t size) { + + osalDbgAssert(!(tdp->physdesc->tdes0 & STM32_TDES0_OWN), + "attempt to write descriptor already owned by DMA"); + + if (size > tdp->size - tdp->offset) + size = tdp->size - tdp->offset; + + if (size > 0) { + memcpy((uint8_t *)(tdp->physdesc->tdes2) + tdp->offset, buf, size); + tdp->offset += size; + } + return size; +} + +/** + * @brief Reads from a receive descriptor's stream. + * + * @param[in] rdp pointer to a @p MACReceiveDescriptor structure + * @param[in] buf pointer to the buffer that will receive the read data + * @param[in] size number of bytes to be read + * @return The number of bytes read from the descriptor's + * stream, this value can be less than the amount + * specified in the parameter @p size if there are + * no more bytes to read. + * + * @notapi + */ +size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp, + uint8_t *buf, + size_t size) { + + osalDbgAssert(!(rdp->physdesc->rdes0 & STM32_RDES0_OWN), + "attempt to read descriptor already owned by DMA"); + + if (size > rdp->size - rdp->offset) + size = rdp->size - rdp->offset; + + if (size > 0) { + memcpy(buf, (uint8_t *)(rdp->physdesc->rdes2) + rdp->offset, size); + rdp->offset += size; + } + return size; +} + +#if MAC_USE_ZERO_COPY || defined(__DOXYGEN__) +/** + * @brief Returns a pointer to the next transmit buffer in the descriptor + * chain. + * @note The API guarantees that enough buffers can be requested to fill + * a whole frame. + * + * @param[in] tdp pointer to a @p MACTransmitDescriptor structure + * @param[in] size size of the requested buffer. Specify the frame size + * on the first call then scale the value down subtracting + * the amount of data already copied into the previous + * buffers. + * @param[out] sizep pointer to variable receiving the buffer size, it is + * zero when the last buffer has already been returned. + * Note that a returned size lower than the amount + * requested means that more buffers must be requested + * in order to fill the frame data entirely. + * @return Pointer to the returned buffer. + * @retval NULL if the buffer chain has been entirely scanned. + * + * @notapi + */ +uint8_t *mac_lld_get_next_transmit_buffer(MACTransmitDescriptor *tdp, + size_t size, + size_t *sizep) { + + if (tdp->offset == 0) { + *sizep = tdp->size; + tdp->offset = size; + return (uint8_t *)tdp->physdesc->tdes2; + } + *sizep = 0; + return NULL; +} + +/** + * @brief Returns a pointer to the next receive buffer in the descriptor + * chain. + * @note The API guarantees that the descriptor chain contains a whole + * frame. + * + * @param[in] rdp pointer to a @p MACReceiveDescriptor structure + * @param[out] sizep pointer to variable receiving the buffer size, it is + * zero when the last buffer has already been returned. + * @return Pointer to the returned buffer. + * @retval NULL if the buffer chain has been entirely scanned. + * + * @notapi + */ +const uint8_t *mac_lld_get_next_receive_buffer(MACReceiveDescriptor *rdp, + size_t *sizep) { + + if (rdp->size > 0) { + *sizep = rdp->size; + rdp->offset = rdp->size; + rdp->size = 0; + return (uint8_t *)rdp->physdesc->rdes2; + } + *sizep = 0; + return NULL; +} +#endif /* MAC_USE_ZERO_COPY */ + +#endif /* HAL_USE_MAC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.h b/os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.h new file mode 100644 index 000000000..0f240168d --- /dev/null +++ b/os/hal/ports/STM32/LLD/MACv1/hal_mac_lld.h @@ -0,0 +1,363 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/mac_lld.h + * @brief STM32 low level MAC driver header. + * + * @addtogroup MAC + * @{ + */ + +#ifndef _MAC_LLD_H_ +#define _MAC_LLD_H_ + +#if HAL_USE_MAC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief This implementation supports the zero-copy mode API. + */ +#define MAC_SUPPORTS_ZERO_COPY TRUE + +/** + * @name RDES0 constants + * @{ + */ +#define STM32_RDES0_OWN 0x80000000 +#define STM32_RDES0_AFM 0x40000000 +#define STM32_RDES0_FL_MASK 0x3FFF0000 +#define STM32_RDES0_ES 0x00008000 +#define STM32_RDES0_DESERR 0x00004000 +#define STM32_RDES0_SAF 0x00002000 +#define STM32_RDES0_LE 0x00001000 +#define STM32_RDES0_OE 0x00000800 +#define STM32_RDES0_VLAN 0x00000400 +#define STM32_RDES0_FS 0x00000200 +#define STM32_RDES0_LS 0x00000100 +#define STM32_RDES0_IPHCE 0x00000080 +#define STM32_RDES0_LCO 0x00000040 +#define STM32_RDES0_FT 0x00000020 +#define STM32_RDES0_RWT 0x00000010 +#define STM32_RDES0_RE 0x00000008 +#define STM32_RDES0_DE 0x00000004 +#define STM32_RDES0_CE 0x00000002 +#define STM32_RDES0_PCE 0x00000001 +/** @} */ + +/** + * @name RDES1 constants + * @{ + */ +#define STM32_RDES1_DIC 0x80000000 +#define STM32_RDES1_RBS2_MASK 0x1FFF0000 +#define STM32_RDES1_RER 0x00008000 +#define STM32_RDES1_RCH 0x00004000 +#define STM32_RDES1_RBS1_MASK 0x00001FFF +/** @} */ + +/** + * @name TDES0 constants + * @{ + */ +#define STM32_TDES0_OWN 0x80000000 +#define STM32_TDES0_IC 0x40000000 +#define STM32_TDES0_LS 0x20000000 +#define STM32_TDES0_FS 0x10000000 +#define STM32_TDES0_DC 0x08000000 +#define STM32_TDES0_DP 0x04000000 +#define STM32_TDES0_TTSE 0x02000000 +#define STM32_TDES0_LOCKED 0x01000000 /* NOTE: Pseudo flag. */ +#define STM32_TDES0_CIC_MASK 0x00C00000 +#define STM32_TDES0_CIC(n) ((n) << 22) +#define STM32_TDES0_TER 0x00200000 +#define STM32_TDES0_TCH 0x00100000 +#define STM32_TDES0_TTSS 0x00020000 +#define STM32_TDES0_IHE 0x00010000 +#define STM32_TDES0_ES 0x00008000 +#define STM32_TDES0_JT 0x00004000 +#define STM32_TDES0_FF 0x00002000 +#define STM32_TDES0_IPE 0x00001000 +#define STM32_TDES0_LCA 0x00000800 +#define STM32_TDES0_NC 0x00000400 +#define STM32_TDES0_LCO 0x00000200 +#define STM32_TDES0_EC 0x00000100 +#define STM32_TDES0_VF 0x00000080 +#define STM32_TDES0_CC_MASK 0x00000078 +#define STM32_TDES0_ED 0x00000004 +#define STM32_TDES0_UF 0x00000002 +#define STM32_TDES0_DB 0x00000001 +/** @} */ + +/** + * @name TDES1 constants + * @{ + */ +#define STM32_TDES1_TBS2_MASK 0x1FFF0000 +#define STM32_TDES1_TBS1_MASK 0x00001FFF +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Number of available transmit buffers. + */ +#if !defined(STM32_MAC_TRANSMIT_BUFFERS) || defined(__DOXYGEN__) +#define STM32_MAC_TRANSMIT_BUFFERS 2 +#endif + +/** + * @brief Number of available receive buffers. + */ +#if !defined(STM32_MAC_RECEIVE_BUFFERS) || defined(__DOXYGEN__) +#define STM32_MAC_RECEIVE_BUFFERS 4 +#endif + +/** + * @brief Maximum supported frame size. + */ +#if !defined(STM32_MAC_BUFFERS_SIZE) || defined(__DOXYGEN__) +#define STM32_MAC_BUFFERS_SIZE 1522 +#endif + +/** + * @brief PHY detection timeout. + * @details Timeout for PHY address detection, the scan for a PHY is performed + * the specified number of times before invoking the failure handler. + * This setting applies only if the PHY address is not explicitly + * set in the board header file using @p BOARD_PHY_ADDRESS. A zero + * value disables the timeout and a single search is performed. + */ +#if !defined(STM32_MAC_PHY_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_MAC_PHY_TIMEOUT 100 +#endif + +/** + * @brief Change the PHY power state inside the driver. + */ +#if !defined(STM32_MAC_ETH1_CHANGE_PHY_STATE) || defined(__DOXYGEN__) +#define STM32_MAC_ETH1_CHANGE_PHY_STATE TRUE +#endif + +/** + * @brief ETHD1 interrupt priority level setting. + */ +#if !defined(STM32_MAC_ETH1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_MAC_ETH1_IRQ_PRIORITY 13 +#endif + +/** + * @brief IP checksum offload. + * @details The following modes are available: + * - 0 Function disabled. + * - 1 Only IP header checksum calculation and insertion are enabled. + * - 2 IP header checksum and payload checksum calculation and + * insertion are enabled, but pseudo-header checksum is not + * calculated in hardware. + * - 3 IP Header checksum and payload checksum calculation and + * insertion are enabled, and pseudo-header checksum is + * calculated in hardware. + * . + */ +#if !defined(STM32_MAC_IP_CHECKSUM_OFFLOAD) || defined(__DOXYGEN__) +#define STM32_MAC_IP_CHECKSUM_OFFLOAD 0 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if (STM32_MAC_PHY_TIMEOUT > 0) && !HAL_IMPLEMENTS_COUNTERS +#error "STM32_MAC_PHY_TIMEOUT requires the realtime counter service" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of an STM32 Ethernet receive descriptor. + */ +typedef struct { + volatile uint32_t rdes0; + volatile uint32_t rdes1; + volatile uint32_t rdes2; + volatile uint32_t rdes3; +} stm32_eth_rx_descriptor_t; + +/** + * @brief Type of an STM32 Ethernet transmit descriptor. + */ +typedef struct { + volatile uint32_t tdes0; + volatile uint32_t tdes1; + volatile uint32_t tdes2; + volatile uint32_t tdes3; +} stm32_eth_tx_descriptor_t; + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief MAC address. + */ + uint8_t *mac_address; + /* End of the mandatory fields.*/ +} MACConfig; + +/** + * @brief Structure representing a MAC driver. + */ +struct MACDriver { + /** + * @brief Driver state. + */ + macstate_t state; + /** + * @brief Current configuration data. + */ + const MACConfig *config; + /** + * @brief Transmit semaphore. + */ + threads_queue_t tdqueue; + /** + * @brief Receive semaphore. + */ + threads_queue_t rdqueue; +#if MAC_USE_EVENTS || defined(__DOXYGEN__) + /** + * @brief Receive event. + */ + event_source_t rdevent; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Link status flag. + */ + bool link_up; + /** + * @brief PHY address (pre shifted). + */ + uint32_t phyaddr; + /** + * @brief Receive next frame pointer. + */ + stm32_eth_rx_descriptor_t *rxptr; + /** + * @brief Transmit next frame pointer. + */ + stm32_eth_tx_descriptor_t *txptr; +}; + +/** + * @brief Structure representing a transmit descriptor. + */ +typedef struct { + /** + * @brief Current write offset. + */ + size_t offset; + /** + * @brief Available space size. + */ + size_t size; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the physical descriptor. + */ + stm32_eth_tx_descriptor_t *physdesc; +} MACTransmitDescriptor; + +/** + * @brief Structure representing a receive descriptor. + */ +typedef struct { + /** + * @brief Current read offset. + */ + size_t offset; + /** + * @brief Available data size. + */ + size_t size; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the physical descriptor. + */ + stm32_eth_rx_descriptor_t *physdesc; +} MACReceiveDescriptor; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern MACDriver ETHD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void mii_write(MACDriver *macp, uint32_t reg, uint32_t value); + uint32_t mii_read(MACDriver *macp, uint32_t reg); + void mac_lld_init(void); + void mac_lld_start(MACDriver *macp); + void mac_lld_stop(MACDriver *macp); + msg_t mac_lld_get_transmit_descriptor(MACDriver *macp, + MACTransmitDescriptor *tdp); + void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp); + msg_t mac_lld_get_receive_descriptor(MACDriver *macp, + MACReceiveDescriptor *rdp); + void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp); + bool mac_lld_poll_link_status(MACDriver *macp); + size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp, + uint8_t *buf, + size_t size); + size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp, + uint8_t *buf, + size_t size); +#if MAC_USE_ZERO_COPY + uint8_t *mac_lld_get_next_transmit_buffer(MACTransmitDescriptor *tdp, + size_t size, + size_t *sizep); + const uint8_t *mac_lld_get_next_receive_buffer(MACReceiveDescriptor *rdp, + size_t *sizep); +#endif /* MAC_USE_ZERO_COPY */ +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_MAC */ + +#endif /* _MAC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/MACv1/mac_lld.c b/os/hal/ports/STM32/LLD/MACv1/mac_lld.c deleted file mode 100644 index a223c00d5..000000000 --- a/os/hal/ports/STM32/LLD/MACv1/mac_lld.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/mac_lld.c - * @brief STM32 low level MAC driver code. - * - * @addtogroup MAC - * @{ - */ - -#include - -#include "hal.h" - -#if HAL_USE_MAC || defined(__DOXYGEN__) - -#include "mii.h" - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define BUFFER_SIZE ((((STM32_MAC_BUFFERS_SIZE - 1) | 3) + 1) / 4) - -/* MII divider optimal value.*/ -#if (STM32_HCLK >= 150000000) -#define MACMIIDR_CR ETH_MACMIIAR_CR_Div102 -#elif (STM32_HCLK >= 100000000) -#define MACMIIDR_CR ETH_MACMIIAR_CR_Div62 -#elif (STM32_HCLK >= 60000000) -#define MACMIIDR_CR ETH_MACMIIAR_CR_Div42 -#elif (STM32_HCLK >= 35000000) -#define MACMIIDR_CR ETH_MACMIIAR_CR_Div26 -#elif (STM32_HCLK >= 20000000) -#define MACMIIDR_CR ETH_MACMIIAR_CR_Div16 -#else -#error "STM32_HCLK below minimum frequency for ETH operations (20MHz)" -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief Ethernet driver 1. - */ -MACDriver ETHD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -static const uint8_t default_mac_address[] = {0xAA, 0x55, 0x13, - 0x37, 0x01, 0x10}; - -static stm32_eth_rx_descriptor_t __eth_rd[STM32_MAC_RECEIVE_BUFFERS]; -static stm32_eth_tx_descriptor_t __eth_td[STM32_MAC_TRANSMIT_BUFFERS]; - -static uint32_t __eth_rb[STM32_MAC_RECEIVE_BUFFERS][BUFFER_SIZE]; -static uint32_t __eth_tb[STM32_MAC_TRANSMIT_BUFFERS][BUFFER_SIZE]; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Writes a PHY register. - * - * @param[in] macp pointer to the @p MACDriver object - * @param[in] reg register number - * @param[in] value new register value - * - * @notapi - */ -void mii_write(MACDriver *macp, uint32_t reg, uint32_t value) { - - ETH->MACMIIDR = value; - ETH->MACMIIAR = macp->phyaddr | (reg << 6) | MACMIIDR_CR | - ETH_MACMIIAR_MW | ETH_MACMIIAR_MB; - while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) != 0) - ; -} - -/** - * @brief Reads a PHY register. - * - * @param[in] macp pointer to the @p MACDriver object - * @param[in] reg register number - * - * @return The PHY register content. - * - * @notapi - */ -uint32_t mii_read(MACDriver *macp, uint32_t reg) { - - ETH->MACMIIAR = macp->phyaddr | (reg << 6) | MACMIIDR_CR | ETH_MACMIIAR_MB; - while ((ETH->MACMIIAR & ETH_MACMIIAR_MB) != 0) - ; - return ETH->MACMIIDR; -} - -#if !defined(BOARD_PHY_ADDRESS) -/** - * @brief PHY address detection. - * - * @param[in] macp pointer to the @p MACDriver object - */ -static void mii_find_phy(MACDriver *macp) { - uint32_t i; - -#if STM32_MAC_PHY_TIMEOUT > 0 - unsigned n = STM32_MAC_PHY_TIMEOUT; - do { -#endif - for (i = 0U; i < 31U; i++) { - macp->phyaddr = i << 11U; - ETH->MACMIIDR = (i << 6U) | MACMIIDR_CR; - if ((mii_read(macp, MII_PHYSID1) == (BOARD_PHY_ID >> 16U)) && - ((mii_read(macp, MII_PHYSID2) & 0xFFF0U) == (BOARD_PHY_ID & 0xFFF0U))) { - return; - } - } -#if STM32_MAC_PHY_TIMEOUT > 0 - n--; - } while (n > 0U); -#endif - /* Wrong or defective board.*/ - osalSysHalt("MAC failure"); -} -#endif - -/** - * @brief MAC address setup. - * - * @param[in] p pointer to a six bytes buffer containing the MAC - * address - */ -static void mac_lld_set_address(const uint8_t *p) { - - /* MAC address configuration, only a single address comparator is used, - hash table not used.*/ - ETH->MACA0HR = ((uint32_t)p[5] << 8) | - ((uint32_t)p[4] << 0); - ETH->MACA0LR = ((uint32_t)p[3] << 24) | - ((uint32_t)p[2] << 16) | - ((uint32_t)p[1] << 8) | - ((uint32_t)p[0] << 0); - ETH->MACA1HR = 0x0000FFFF; - ETH->MACA1LR = 0xFFFFFFFF; - ETH->MACA2HR = 0x0000FFFF; - ETH->MACA2LR = 0xFFFFFFFF; - ETH->MACA3HR = 0x0000FFFF; - ETH->MACA3LR = 0xFFFFFFFF; - ETH->MACHTHR = 0; - ETH->MACHTLR = 0; -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -OSAL_IRQ_HANDLER(STM32_ETH_HANDLER) { - uint32_t dmasr; - - OSAL_IRQ_PROLOGUE(); - - dmasr = ETH->DMASR; - ETH->DMASR = dmasr; /* Clear status bits.*/ - - if (dmasr & ETH_DMASR_RS) { - /* Data Received.*/ - osalSysLockFromISR(); - osalThreadDequeueAllI(ÐD1.rdqueue, MSG_RESET); -#if MAC_USE_EVENTS - osalEventBroadcastFlagsI(ÐD1.rdevent, 0); -#endif - osalSysUnlockFromISR(); - } - - if (dmasr & ETH_DMASR_TS) { - /* Data Transmitted.*/ - osalSysLockFromISR(); - osalThreadDequeueAllI(ÐD1.tdqueue, MSG_RESET); - osalSysUnlockFromISR(); - } - - OSAL_IRQ_EPILOGUE(); -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level MAC initialization. - * - * @notapi - */ -void mac_lld_init(void) { - unsigned i; - - macObjectInit(ÐD1); - ETHD1.link_up = false; - - /* Descriptor tables are initialized in chained mode, note that the first - word is not initialized here but in mac_lld_start().*/ - for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) { - __eth_rd[i].rdes1 = STM32_RDES1_RCH | STM32_MAC_BUFFERS_SIZE; - __eth_rd[i].rdes2 = (uint32_t)__eth_rb[i]; - __eth_rd[i].rdes3 = (uint32_t)&__eth_rd[(i + 1) % STM32_MAC_RECEIVE_BUFFERS]; - } - for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) { - __eth_td[i].tdes1 = 0; - __eth_td[i].tdes2 = (uint32_t)__eth_tb[i]; - __eth_td[i].tdes3 = (uint32_t)&__eth_td[(i + 1) % STM32_MAC_TRANSMIT_BUFFERS]; - } - - /* Selection of the RMII or MII mode based on info exported by board.h.*/ -#if defined(STM32F10X_CL) -#if defined(BOARD_PHY_RMII) - AFIO->MAPR |= AFIO_MAPR_MII_RMII_SEL; -#else - AFIO->MAPR &= ~AFIO_MAPR_MII_RMII_SEL; -#endif -#elif defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) -#if defined(BOARD_PHY_RMII) - SYSCFG->PMC |= SYSCFG_PMC_MII_RMII_SEL; -#else - SYSCFG->PMC &= ~SYSCFG_PMC_MII_RMII_SEL; -#endif -#else -#error "unsupported STM32 platform for MAC driver" -#endif - - /* Reset of the MAC core.*/ - rccResetETH(); - - /* MAC clocks temporary activation.*/ - rccEnableETH(false); - - /* PHY address setup.*/ -#if defined(BOARD_PHY_ADDRESS) - ETHD1.phyaddr = BOARD_PHY_ADDRESS << 11; -#else - mii_find_phy(ÐD1); -#endif - -#if defined(BOARD_PHY_RESET) - /* PHY board-specific reset procedure.*/ - BOARD_PHY_RESET(); -#else - /* PHY soft reset procedure.*/ - mii_write(ÐD1, MII_BMCR, BMCR_RESET); -#if defined(BOARD_PHY_RESET_DELAY) - osalSysPolledDelayX(BOARD_PHY_RESET_DELAY); -#endif - while (mii_read(ÐD1, MII_BMCR) & BMCR_RESET) - ; -#endif - -#if STM32_MAC_ETH1_CHANGE_PHY_STATE - /* PHY in power down mode until the driver will be started.*/ - mii_write(ÐD1, MII_BMCR, mii_read(ÐD1, MII_BMCR) | BMCR_PDOWN); -#endif - - /* MAC clocks stopped again.*/ - rccDisableETH(false); -} - -/** - * @brief Configures and activates the MAC peripheral. - * - * @param[in] macp pointer to the @p MACDriver object - * - * @notapi - */ -void mac_lld_start(MACDriver *macp) { - unsigned i; - - /* Resets the state of all descriptors.*/ - for (i = 0; i < STM32_MAC_RECEIVE_BUFFERS; i++) - __eth_rd[i].rdes0 = STM32_RDES0_OWN; - macp->rxptr = (stm32_eth_rx_descriptor_t *)__eth_rd; - for (i = 0; i < STM32_MAC_TRANSMIT_BUFFERS; i++) - __eth_td[i].tdes0 = STM32_TDES0_TCH; - macp->txptr = (stm32_eth_tx_descriptor_t *)__eth_td; - - /* MAC clocks activation and commanded reset procedure.*/ - rccEnableETH(false); -#if defined(STM32_MAC_DMABMR_SR) - ETH->DMABMR |= ETH_DMABMR_SR; - while(ETH->DMABMR & ETH_DMABMR_SR) - ; -#endif - - /* ISR vector enabled.*/ - nvicEnableVector(STM32_ETH_NUMBER, STM32_MAC_ETH1_IRQ_PRIORITY); - -#if STM32_MAC_ETH1_CHANGE_PHY_STATE - /* PHY in power up mode.*/ - mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) & ~BMCR_PDOWN); -#endif - - /* MAC configuration.*/ - ETH->MACFFR = 0; - ETH->MACFCR = 0; - ETH->MACVLANTR = 0; - - /* MAC address setup.*/ - if (macp->config->mac_address == NULL) - mac_lld_set_address(default_mac_address); - else - mac_lld_set_address(macp->config->mac_address); - - /* Transmitter and receiver enabled. - Note that the complete setup of the MAC is performed when the link - status is detected.*/ -#if STM32_MAC_IP_CHECKSUM_OFFLOAD - ETH->MACCR = ETH_MACCR_IPCO | ETH_MACCR_RE | ETH_MACCR_TE; -#else - ETH->MACCR = ETH_MACCR_RE | ETH_MACCR_TE; -#endif - - /* DMA configuration: - Descriptor chains pointers.*/ - ETH->DMARDLAR = (uint32_t)__eth_rd; - ETH->DMATDLAR = (uint32_t)__eth_td; - - /* Enabling required interrupt sources.*/ - ETH->DMASR = ETH->DMASR; - ETH->DMAIER = ETH_DMAIER_NISE | ETH_DMAIER_RIE | ETH_DMAIER_TIE; - - /* DMA general settings.*/ - ETH->DMABMR = ETH_DMABMR_AAB | ETH_DMABMR_RDP_1Beat | ETH_DMABMR_PBL_1Beat; - - /* Transmit FIFO flush.*/ - ETH->DMAOMR = ETH_DMAOMR_FTF; - while (ETH->DMAOMR & ETH_DMAOMR_FTF) - ; - - /* DMA final configuration and start.*/ - ETH->DMAOMR = ETH_DMAOMR_DTCEFD | ETH_DMAOMR_RSF | ETH_DMAOMR_TSF | - ETH_DMAOMR_ST | ETH_DMAOMR_SR; -} - -/** - * @brief Deactivates the MAC peripheral. - * - * @param[in] macp pointer to the @p MACDriver object - * - * @notapi - */ -void mac_lld_stop(MACDriver *macp) { - - if (macp->state != MAC_STOP) { -#if STM32_MAC_ETH1_CHANGE_PHY_STATE - /* PHY in power down mode until the driver will be restarted.*/ - mii_write(macp, MII_BMCR, mii_read(macp, MII_BMCR) | BMCR_PDOWN); -#endif - - /* MAC and DMA stopped.*/ - ETH->MACCR = 0; - ETH->DMAOMR = 0; - ETH->DMAIER = 0; - ETH->DMASR = ETH->DMASR; - - /* MAC clocks stopped.*/ - rccDisableETH(false); - - /* ISR vector disabled.*/ - nvicDisableVector(STM32_ETH_NUMBER); - } -} - -/** - * @brief Returns a transmission descriptor. - * @details One of the available transmission descriptors is locked and - * returned. - * - * @param[in] macp pointer to the @p MACDriver object - * @param[out] tdp pointer to a @p MACTransmitDescriptor structure - * @return The operation status. - * @retval MSG_OK the descriptor has been obtained. - * @retval MSG_TIMEOUT descriptor not available. - * - * @notapi - */ -msg_t mac_lld_get_transmit_descriptor(MACDriver *macp, - MACTransmitDescriptor *tdp) { - stm32_eth_tx_descriptor_t *tdes; - - if (!macp->link_up) - return MSG_TIMEOUT; - - osalSysLock(); - - /* Get Current TX descriptor.*/ - tdes = macp->txptr; - - /* Ensure that descriptor isn't owned by the Ethernet DMA or locked by - another thread.*/ - if (tdes->tdes0 & (STM32_TDES0_OWN | STM32_TDES0_LOCKED)) { - osalSysUnlock(); - return MSG_TIMEOUT; - } - - /* Marks the current descriptor as locked using a reserved bit.*/ - tdes->tdes0 |= STM32_TDES0_LOCKED; - - /* Next TX descriptor to use.*/ - macp->txptr = (stm32_eth_tx_descriptor_t *)tdes->tdes3; - - osalSysUnlock(); - - /* Set the buffer size and configuration.*/ - tdp->offset = 0; - tdp->size = STM32_MAC_BUFFERS_SIZE; - tdp->physdesc = tdes; - - return MSG_OK; -} - -/** - * @brief Releases a transmit descriptor and starts the transmission of the - * enqueued data as a single frame. - * - * @param[in] tdp the pointer to the @p MACTransmitDescriptor structure - * - * @notapi - */ -void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp) { - - osalDbgAssert(!(tdp->physdesc->tdes0 & STM32_TDES0_OWN), - "attempt to release descriptor already owned by DMA"); - - osalSysLock(); - - /* Unlocks the descriptor and returns it to the DMA engine.*/ - tdp->physdesc->tdes1 = tdp->offset; - tdp->physdesc->tdes0 = STM32_TDES0_CIC(STM32_MAC_IP_CHECKSUM_OFFLOAD) | - STM32_TDES0_IC | STM32_TDES0_LS | STM32_TDES0_FS | - STM32_TDES0_TCH | STM32_TDES0_OWN; - - /* If the DMA engine is stalled then a restart request is issued.*/ - if ((ETH->DMASR & ETH_DMASR_TPS) == ETH_DMASR_TPS_Suspended) { - ETH->DMASR = ETH_DMASR_TBUS; - ETH->DMATPDR = ETH_DMASR_TBUS; /* Any value is OK.*/ - } - - osalSysUnlock(); -} - -/** - * @brief Returns a receive descriptor. - * - * @param[in] macp pointer to the @p MACDriver object - * @param[out] rdp pointer to a @p MACReceiveDescriptor structure - * @return The operation status. - * @retval MSG_OK the descriptor has been obtained. - * @retval MSG_TIMEOUT descriptor not available. - * - * @notapi - */ -msg_t mac_lld_get_receive_descriptor(MACDriver *macp, - MACReceiveDescriptor *rdp) { - stm32_eth_rx_descriptor_t *rdes; - - osalSysLock(); - - /* Get Current RX descriptor.*/ - rdes = macp->rxptr; - - /* Iterates through received frames until a valid one is found, invalid - frames are discarded.*/ - while (!(rdes->rdes0 & STM32_RDES0_OWN)) { - if (!(rdes->rdes0 & (STM32_RDES0_AFM | STM32_RDES0_ES)) -#if STM32_MAC_IP_CHECKSUM_OFFLOAD - && (rdes->rdes0 & STM32_RDES0_FT) - && !(rdes->rdes0 & (STM32_RDES0_IPHCE | STM32_RDES0_PCE)) -#endif - && (rdes->rdes0 & STM32_RDES0_FS) && (rdes->rdes0 & STM32_RDES0_LS)) { - /* Found a valid one.*/ - rdp->offset = 0; - rdp->size = ((rdes->rdes0 & STM32_RDES0_FL_MASK) >> 16) - 4; - rdp->physdesc = rdes; - macp->rxptr = (stm32_eth_rx_descriptor_t *)rdes->rdes3; - - osalSysUnlock(); - return MSG_OK; - } - /* Invalid frame found, purging.*/ - rdes->rdes0 = STM32_RDES0_OWN; - rdes = (stm32_eth_rx_descriptor_t *)rdes->rdes3; - } - - /* Next descriptor to check.*/ - macp->rxptr = rdes; - - osalSysUnlock(); - return MSG_TIMEOUT; -} - -/** - * @brief Releases a receive descriptor. - * @details The descriptor and its buffer are made available for more incoming - * frames. - * - * @param[in] rdp the pointer to the @p MACReceiveDescriptor structure - * - * @notapi - */ -void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp) { - - osalDbgAssert(!(rdp->physdesc->rdes0 & STM32_RDES0_OWN), - "attempt to release descriptor already owned by DMA"); - - osalSysLock(); - - /* Give buffer back to the Ethernet DMA.*/ - rdp->physdesc->rdes0 = STM32_RDES0_OWN; - - /* If the DMA engine is stalled then a restart request is issued.*/ - if ((ETH->DMASR & ETH_DMASR_RPS) == ETH_DMASR_RPS_Suspended) { - ETH->DMASR = ETH_DMASR_RBUS; - ETH->DMARPDR = ETH_DMASR_RBUS; /* Any value is OK.*/ - } - - osalSysUnlock(); -} - -/** - * @brief Updates and returns the link status. - * - * @param[in] macp pointer to the @p MACDriver object - * @return The link status. - * @retval true if the link is active. - * @retval false if the link is down. - * - * @notapi - */ -bool mac_lld_poll_link_status(MACDriver *macp) { - uint32_t maccr, bmsr, bmcr; - - maccr = ETH->MACCR; - - /* PHY CR and SR registers read.*/ - (void)mii_read(macp, MII_BMSR); - bmsr = mii_read(macp, MII_BMSR); - bmcr = mii_read(macp, MII_BMCR); - - /* Check on auto-negotiation mode.*/ - if (bmcr & BMCR_ANENABLE) { - uint32_t lpa; - - /* Auto-negotiation must be finished without faults and link established.*/ - if ((bmsr & (BMSR_LSTATUS | BMSR_RFAULT | BMSR_ANEGCOMPLETE)) != - (BMSR_LSTATUS | BMSR_ANEGCOMPLETE)) - return macp->link_up = false; - - /* Auto-negotiation enabled, checks the LPA register.*/ - lpa = mii_read(macp, MII_LPA); - - /* Check on link speed.*/ - if (lpa & (LPA_100HALF | LPA_100FULL | LPA_100BASE4)) - maccr |= ETH_MACCR_FES; - else - maccr &= ~ETH_MACCR_FES; - - /* Check on link mode.*/ - if (lpa & (LPA_10FULL | LPA_100FULL)) - maccr |= ETH_MACCR_DM; - else - maccr &= ~ETH_MACCR_DM; - } - else { - /* Link must be established.*/ - if (!(bmsr & BMSR_LSTATUS)) - return macp->link_up = false; - - /* Check on link speed.*/ - if (bmcr & BMCR_SPEED100) - maccr |= ETH_MACCR_FES; - else - maccr &= ~ETH_MACCR_FES; - - /* Check on link mode.*/ - if (bmcr & BMCR_FULLDPLX) - maccr |= ETH_MACCR_DM; - else - maccr &= ~ETH_MACCR_DM; - } - - /* Changes the mode in the MAC.*/ - ETH->MACCR = maccr; - - /* Returns the link status.*/ - return macp->link_up = true; -} - -/** - * @brief Writes to a transmit descriptor's stream. - * - * @param[in] tdp pointer to a @p MACTransmitDescriptor structure - * @param[in] buf pointer to the buffer containing the data to be - * written - * @param[in] size number of bytes to be written - * @return The number of bytes written into the descriptor's - * stream, this value can be less than the amount - * specified in the parameter @p size if the maximum - * frame size is reached. - * - * @notapi - */ -size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp, - uint8_t *buf, - size_t size) { - - osalDbgAssert(!(tdp->physdesc->tdes0 & STM32_TDES0_OWN), - "attempt to write descriptor already owned by DMA"); - - if (size > tdp->size - tdp->offset) - size = tdp->size - tdp->offset; - - if (size > 0) { - memcpy((uint8_t *)(tdp->physdesc->tdes2) + tdp->offset, buf, size); - tdp->offset += size; - } - return size; -} - -/** - * @brief Reads from a receive descriptor's stream. - * - * @param[in] rdp pointer to a @p MACReceiveDescriptor structure - * @param[in] buf pointer to the buffer that will receive the read data - * @param[in] size number of bytes to be read - * @return The number of bytes read from the descriptor's - * stream, this value can be less than the amount - * specified in the parameter @p size if there are - * no more bytes to read. - * - * @notapi - */ -size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp, - uint8_t *buf, - size_t size) { - - osalDbgAssert(!(rdp->physdesc->rdes0 & STM32_RDES0_OWN), - "attempt to read descriptor already owned by DMA"); - - if (size > rdp->size - rdp->offset) - size = rdp->size - rdp->offset; - - if (size > 0) { - memcpy(buf, (uint8_t *)(rdp->physdesc->rdes2) + rdp->offset, size); - rdp->offset += size; - } - return size; -} - -#if MAC_USE_ZERO_COPY || defined(__DOXYGEN__) -/** - * @brief Returns a pointer to the next transmit buffer in the descriptor - * chain. - * @note The API guarantees that enough buffers can be requested to fill - * a whole frame. - * - * @param[in] tdp pointer to a @p MACTransmitDescriptor structure - * @param[in] size size of the requested buffer. Specify the frame size - * on the first call then scale the value down subtracting - * the amount of data already copied into the previous - * buffers. - * @param[out] sizep pointer to variable receiving the buffer size, it is - * zero when the last buffer has already been returned. - * Note that a returned size lower than the amount - * requested means that more buffers must be requested - * in order to fill the frame data entirely. - * @return Pointer to the returned buffer. - * @retval NULL if the buffer chain has been entirely scanned. - * - * @notapi - */ -uint8_t *mac_lld_get_next_transmit_buffer(MACTransmitDescriptor *tdp, - size_t size, - size_t *sizep) { - - if (tdp->offset == 0) { - *sizep = tdp->size; - tdp->offset = size; - return (uint8_t *)tdp->physdesc->tdes2; - } - *sizep = 0; - return NULL; -} - -/** - * @brief Returns a pointer to the next receive buffer in the descriptor - * chain. - * @note The API guarantees that the descriptor chain contains a whole - * frame. - * - * @param[in] rdp pointer to a @p MACReceiveDescriptor structure - * @param[out] sizep pointer to variable receiving the buffer size, it is - * zero when the last buffer has already been returned. - * @return Pointer to the returned buffer. - * @retval NULL if the buffer chain has been entirely scanned. - * - * @notapi - */ -const uint8_t *mac_lld_get_next_receive_buffer(MACReceiveDescriptor *rdp, - size_t *sizep) { - - if (rdp->size > 0) { - *sizep = rdp->size; - rdp->offset = rdp->size; - rdp->size = 0; - return (uint8_t *)rdp->physdesc->rdes2; - } - *sizep = 0; - return NULL; -} -#endif /* MAC_USE_ZERO_COPY */ - -#endif /* HAL_USE_MAC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/MACv1/mac_lld.h b/os/hal/ports/STM32/LLD/MACv1/mac_lld.h deleted file mode 100644 index 0f240168d..000000000 --- a/os/hal/ports/STM32/LLD/MACv1/mac_lld.h +++ /dev/null @@ -1,363 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/mac_lld.h - * @brief STM32 low level MAC driver header. - * - * @addtogroup MAC - * @{ - */ - -#ifndef _MAC_LLD_H_ -#define _MAC_LLD_H_ - -#if HAL_USE_MAC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief This implementation supports the zero-copy mode API. - */ -#define MAC_SUPPORTS_ZERO_COPY TRUE - -/** - * @name RDES0 constants - * @{ - */ -#define STM32_RDES0_OWN 0x80000000 -#define STM32_RDES0_AFM 0x40000000 -#define STM32_RDES0_FL_MASK 0x3FFF0000 -#define STM32_RDES0_ES 0x00008000 -#define STM32_RDES0_DESERR 0x00004000 -#define STM32_RDES0_SAF 0x00002000 -#define STM32_RDES0_LE 0x00001000 -#define STM32_RDES0_OE 0x00000800 -#define STM32_RDES0_VLAN 0x00000400 -#define STM32_RDES0_FS 0x00000200 -#define STM32_RDES0_LS 0x00000100 -#define STM32_RDES0_IPHCE 0x00000080 -#define STM32_RDES0_LCO 0x00000040 -#define STM32_RDES0_FT 0x00000020 -#define STM32_RDES0_RWT 0x00000010 -#define STM32_RDES0_RE 0x00000008 -#define STM32_RDES0_DE 0x00000004 -#define STM32_RDES0_CE 0x00000002 -#define STM32_RDES0_PCE 0x00000001 -/** @} */ - -/** - * @name RDES1 constants - * @{ - */ -#define STM32_RDES1_DIC 0x80000000 -#define STM32_RDES1_RBS2_MASK 0x1FFF0000 -#define STM32_RDES1_RER 0x00008000 -#define STM32_RDES1_RCH 0x00004000 -#define STM32_RDES1_RBS1_MASK 0x00001FFF -/** @} */ - -/** - * @name TDES0 constants - * @{ - */ -#define STM32_TDES0_OWN 0x80000000 -#define STM32_TDES0_IC 0x40000000 -#define STM32_TDES0_LS 0x20000000 -#define STM32_TDES0_FS 0x10000000 -#define STM32_TDES0_DC 0x08000000 -#define STM32_TDES0_DP 0x04000000 -#define STM32_TDES0_TTSE 0x02000000 -#define STM32_TDES0_LOCKED 0x01000000 /* NOTE: Pseudo flag. */ -#define STM32_TDES0_CIC_MASK 0x00C00000 -#define STM32_TDES0_CIC(n) ((n) << 22) -#define STM32_TDES0_TER 0x00200000 -#define STM32_TDES0_TCH 0x00100000 -#define STM32_TDES0_TTSS 0x00020000 -#define STM32_TDES0_IHE 0x00010000 -#define STM32_TDES0_ES 0x00008000 -#define STM32_TDES0_JT 0x00004000 -#define STM32_TDES0_FF 0x00002000 -#define STM32_TDES0_IPE 0x00001000 -#define STM32_TDES0_LCA 0x00000800 -#define STM32_TDES0_NC 0x00000400 -#define STM32_TDES0_LCO 0x00000200 -#define STM32_TDES0_EC 0x00000100 -#define STM32_TDES0_VF 0x00000080 -#define STM32_TDES0_CC_MASK 0x00000078 -#define STM32_TDES0_ED 0x00000004 -#define STM32_TDES0_UF 0x00000002 -#define STM32_TDES0_DB 0x00000001 -/** @} */ - -/** - * @name TDES1 constants - * @{ - */ -#define STM32_TDES1_TBS2_MASK 0x1FFF0000 -#define STM32_TDES1_TBS1_MASK 0x00001FFF -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief Number of available transmit buffers. - */ -#if !defined(STM32_MAC_TRANSMIT_BUFFERS) || defined(__DOXYGEN__) -#define STM32_MAC_TRANSMIT_BUFFERS 2 -#endif - -/** - * @brief Number of available receive buffers. - */ -#if !defined(STM32_MAC_RECEIVE_BUFFERS) || defined(__DOXYGEN__) -#define STM32_MAC_RECEIVE_BUFFERS 4 -#endif - -/** - * @brief Maximum supported frame size. - */ -#if !defined(STM32_MAC_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define STM32_MAC_BUFFERS_SIZE 1522 -#endif - -/** - * @brief PHY detection timeout. - * @details Timeout for PHY address detection, the scan for a PHY is performed - * the specified number of times before invoking the failure handler. - * This setting applies only if the PHY address is not explicitly - * set in the board header file using @p BOARD_PHY_ADDRESS. A zero - * value disables the timeout and a single search is performed. - */ -#if !defined(STM32_MAC_PHY_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_MAC_PHY_TIMEOUT 100 -#endif - -/** - * @brief Change the PHY power state inside the driver. - */ -#if !defined(STM32_MAC_ETH1_CHANGE_PHY_STATE) || defined(__DOXYGEN__) -#define STM32_MAC_ETH1_CHANGE_PHY_STATE TRUE -#endif - -/** - * @brief ETHD1 interrupt priority level setting. - */ -#if !defined(STM32_MAC_ETH1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_MAC_ETH1_IRQ_PRIORITY 13 -#endif - -/** - * @brief IP checksum offload. - * @details The following modes are available: - * - 0 Function disabled. - * - 1 Only IP header checksum calculation and insertion are enabled. - * - 2 IP header checksum and payload checksum calculation and - * insertion are enabled, but pseudo-header checksum is not - * calculated in hardware. - * - 3 IP Header checksum and payload checksum calculation and - * insertion are enabled, and pseudo-header checksum is - * calculated in hardware. - * . - */ -#if !defined(STM32_MAC_IP_CHECKSUM_OFFLOAD) || defined(__DOXYGEN__) -#define STM32_MAC_IP_CHECKSUM_OFFLOAD 0 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if (STM32_MAC_PHY_TIMEOUT > 0) && !HAL_IMPLEMENTS_COUNTERS -#error "STM32_MAC_PHY_TIMEOUT requires the realtime counter service" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of an STM32 Ethernet receive descriptor. - */ -typedef struct { - volatile uint32_t rdes0; - volatile uint32_t rdes1; - volatile uint32_t rdes2; - volatile uint32_t rdes3; -} stm32_eth_rx_descriptor_t; - -/** - * @brief Type of an STM32 Ethernet transmit descriptor. - */ -typedef struct { - volatile uint32_t tdes0; - volatile uint32_t tdes1; - volatile uint32_t tdes2; - volatile uint32_t tdes3; -} stm32_eth_tx_descriptor_t; - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /** - * @brief MAC address. - */ - uint8_t *mac_address; - /* End of the mandatory fields.*/ -} MACConfig; - -/** - * @brief Structure representing a MAC driver. - */ -struct MACDriver { - /** - * @brief Driver state. - */ - macstate_t state; - /** - * @brief Current configuration data. - */ - const MACConfig *config; - /** - * @brief Transmit semaphore. - */ - threads_queue_t tdqueue; - /** - * @brief Receive semaphore. - */ - threads_queue_t rdqueue; -#if MAC_USE_EVENTS || defined(__DOXYGEN__) - /** - * @brief Receive event. - */ - event_source_t rdevent; -#endif - /* End of the mandatory fields.*/ - /** - * @brief Link status flag. - */ - bool link_up; - /** - * @brief PHY address (pre shifted). - */ - uint32_t phyaddr; - /** - * @brief Receive next frame pointer. - */ - stm32_eth_rx_descriptor_t *rxptr; - /** - * @brief Transmit next frame pointer. - */ - stm32_eth_tx_descriptor_t *txptr; -}; - -/** - * @brief Structure representing a transmit descriptor. - */ -typedef struct { - /** - * @brief Current write offset. - */ - size_t offset; - /** - * @brief Available space size. - */ - size_t size; - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the physical descriptor. - */ - stm32_eth_tx_descriptor_t *physdesc; -} MACTransmitDescriptor; - -/** - * @brief Structure representing a receive descriptor. - */ -typedef struct { - /** - * @brief Current read offset. - */ - size_t offset; - /** - * @brief Available data size. - */ - size_t size; - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the physical descriptor. - */ - stm32_eth_rx_descriptor_t *physdesc; -} MACReceiveDescriptor; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern MACDriver ETHD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void mii_write(MACDriver *macp, uint32_t reg, uint32_t value); - uint32_t mii_read(MACDriver *macp, uint32_t reg); - void mac_lld_init(void); - void mac_lld_start(MACDriver *macp); - void mac_lld_stop(MACDriver *macp); - msg_t mac_lld_get_transmit_descriptor(MACDriver *macp, - MACTransmitDescriptor *tdp); - void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp); - msg_t mac_lld_get_receive_descriptor(MACDriver *macp, - MACReceiveDescriptor *rdp); - void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp); - bool mac_lld_poll_link_status(MACDriver *macp); - size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp, - uint8_t *buf, - size_t size); - size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp, - uint8_t *buf, - size_t size); -#if MAC_USE_ZERO_COPY - uint8_t *mac_lld_get_next_transmit_buffer(MACTransmitDescriptor *tdp, - size_t size, - size_t *sizep); - const uint8_t *mac_lld_get_next_receive_buffer(MACReceiveDescriptor *rdp, - size_t *sizep); -#endif /* MAC_USE_ZERO_COPY */ -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_MAC */ - -#endif /* _MAC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.c b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.c new file mode 100644 index 000000000..1c4878b25 --- /dev/null +++ b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.c @@ -0,0 +1,1341 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/OTGv1/usb_lld.c + * @brief STM32 USB subsystem low level driver source. + * + * @addtogroup USB + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define TRDT_VALUE_FS 5 +#define TRDT_VALUE_HS 9 + +#define EP0_MAX_INSIZE 64 +#define EP0_MAX_OUTSIZE 64 + +#if defined(STM32F7XX) +#define GCCFG_INIT_VALUE GCCFG_PWRDWN +#else +#if defined(BOARD_OTG_NOVBUSSENS) +#define GCCFG_INIT_VALUE (GCCFG_NOVBUSSENS | GCCFG_VBUSASEN | \ + GCCFG_VBUSBSEN | GCCFG_PWRDWN) +#else +#define GCCFG_INIT_VALUE (GCCFG_VBUSASEN | GCCFG_VBUSBSEN | \ + GCCFG_PWRDWN) +#endif +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief OTG_FS driver identifier.*/ +#if STM32_USB_USE_OTG1 || defined(__DOXYGEN__) +USBDriver USBD1; +#endif + +/** @brief OTG_HS driver identifier.*/ +#if STM32_USB_USE_OTG2 || defined(__DOXYGEN__) +USBDriver USBD2; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief EP0 state. + * @note It is an union because IN and OUT endpoints are never used at the + * same time for EP0. + */ +static union { + /** + * @brief IN EP0 state. + */ + USBInEndpointState in; + /** + * @brief OUT EP0 state. + */ + USBOutEndpointState out; +} ep0_state; + +/** + * @brief Buffer for the EP0 setup packets. + */ +static uint8_t ep0setup_buffer[8]; + +/** + * @brief EP0 initialization structure. + */ +static const USBEndpointConfig ep0config = { + USB_EP_MODE_TYPE_CTRL, + _usb_ep0setup, + _usb_ep0in, + _usb_ep0out, + 0x40, + 0x40, + &ep0_state.in, + &ep0_state.out, + 1, + ep0setup_buffer +}; + +#if STM32_USB_USE_OTG1 +static const stm32_otg_params_t fsparams = { + STM32_USB_OTG1_RX_FIFO_SIZE / 4, + STM32_OTG1_FIFO_MEM_SIZE, + STM32_OTG1_ENDOPOINTS_NUMBER +}; +#endif + +#if STM32_USB_USE_OTG2 +static const stm32_otg_params_t hsparams = { + STM32_USB_OTG2_RX_FIFO_SIZE / 4, + STM32_OTG2_FIFO_MEM_SIZE, + STM32_OTG2_ENDOPOINTS_NUMBER +}; +#endif + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static void otg_core_reset(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + osalSysPolledDelayX(32); + + /* Core reset and delay of at least 3 PHY cycles.*/ + otgp->GRSTCTL = GRSTCTL_CSRST; + while ((otgp->GRSTCTL & GRSTCTL_CSRST) != 0) + ; + + osalSysPolledDelayX(18); + + /* Wait AHB idle condition.*/ + while ((otgp->GRSTCTL & GRSTCTL_AHBIDL) == 0) + ; +} + +static void otg_disable_ep(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + unsigned i; + + for (i = 0; i <= usbp->otgparams->num_endpoints; i++) { + otgp->ie[i].DIEPCTL = 0; + otgp->ie[i].DIEPTSIZ = 0; + otgp->ie[i].DIEPINT = 0xFFFFFFFF; + + otgp->oe[i].DOEPCTL = 0; + otgp->oe[i].DOEPTSIZ = 0; + otgp->oe[i].DOEPINT = 0xFFFFFFFF; + } + otgp->DAINTMSK = DAINTMSK_OEPM(0) | DAINTMSK_IEPM(0); +} + +static void otg_rxfifo_flush(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + otgp->GRSTCTL = GRSTCTL_RXFFLSH; + while ((otgp->GRSTCTL & GRSTCTL_RXFFLSH) != 0) + ; + /* Wait for 3 PHY Clocks.*/ + osalSysPolledDelayX(18); +} + +static void otg_txfifo_flush(USBDriver *usbp, uint32_t fifo) { + stm32_otg_t *otgp = usbp->otg; + + otgp->GRSTCTL = GRSTCTL_TXFNUM(fifo) | GRSTCTL_TXFFLSH; + while ((otgp->GRSTCTL & GRSTCTL_TXFFLSH) != 0) + ; + /* Wait for 3 PHY Clocks.*/ + osalSysPolledDelayX(18); +} + +/** + * @brief Resets the FIFO RAM memory allocator. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_ram_reset(USBDriver *usbp) { + + usbp->pmnext = usbp->otgparams->rx_fifo_size; +} + +/** + * @brief Allocates a block from the FIFO RAM memory. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] size size of the packet buffer to allocate in words + * + * @notapi + */ +static uint32_t otg_ram_alloc(USBDriver *usbp, size_t size) { + uint32_t next; + + next = usbp->pmnext; + usbp->pmnext += size; + osalDbgAssert(usbp->pmnext <= usbp->otgparams->otg_ram_size, + "OTG FIFO memory overflow"); + return next; +} + +/** + * @brief Writes to a TX FIFO. + * + * @param[in] fifop pointer to the FIFO register + * @param[in] buf buffer where to copy the endpoint data + * @param[in] n maximum number of bytes to copy + * + * @notapi + */ +static void otg_fifo_write_from_buffer(volatile uint32_t *fifop, + const uint8_t *buf, + size_t n) { + + osalDbgAssert(n > 0, "is zero"); + + while (true) { + *fifop = *((uint32_t *)buf); + if (n <= 4) { + break; + } + n -= 4; + buf += 4; + } +} + +/** + * @brief Reads a packet from the RXFIFO. + * + * @param[in] fifop pointer to the FIFO register + * @param[out] buf buffer where to copy the endpoint data + * @param[in] n number of bytes to pull from the FIFO + * @param[in] max number of bytes to copy into the buffer + * + * @notapi + */ +static void otg_fifo_read_to_buffer(volatile uint32_t *fifop, + uint8_t *buf, + size_t n, + size_t max) { + uint32_t w = 0; + size_t i = 0; + + while (i < n) { + if ((i & 3) == 0){ + w = *fifop; + } + if (i < max) { + *buf++ = (uint8_t)w; + w >>= 8; + } + i++; + } +} + +/** + * @brief Incoming packets handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_rxfifo_handler(USBDriver *usbp) { + uint32_t sts, cnt, ep; + + sts = usbp->otg->GRXSTSP; + switch (sts & GRXSTSP_PKTSTS_MASK) { + case GRXSTSP_SETUP_COMP: + break; + case GRXSTSP_SETUP_DATA: + cnt = (sts & GRXSTSP_BCNT_MASK) >> GRXSTSP_BCNT_OFF; + ep = (sts & GRXSTSP_EPNUM_MASK) >> GRXSTSP_EPNUM_OFF; + otg_fifo_read_to_buffer(usbp->otg->FIFO[0], usbp->epc[ep]->setup_buf, + cnt, 8); + break; + case GRXSTSP_OUT_DATA: + cnt = (sts & GRXSTSP_BCNT_MASK) >> GRXSTSP_BCNT_OFF; + ep = (sts & GRXSTSP_EPNUM_MASK) >> GRXSTSP_EPNUM_OFF; + otg_fifo_read_to_buffer(usbp->otg->FIFO[0], + usbp->epc[ep]->out_state->rxbuf, + cnt, + usbp->epc[ep]->out_state->rxsize - + usbp->epc[ep]->out_state->rxcnt); + usbp->epc[ep]->out_state->rxbuf += cnt; + usbp->epc[ep]->out_state->rxcnt += cnt; + break; + case GRXSTSP_OUT_GLOBAL_NAK: + case GRXSTSP_OUT_COMP: + default: + ; + } +} + +/** + * @brief Outgoing packets handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static bool otg_txfifo_handler(USBDriver *usbp, usbep_t ep) { + + /* The TXFIFO is filled until there is space and data to be transmitted.*/ + while (true) { + uint32_t n; + + /* Transaction end condition.*/ + if (usbp->epc[ep]->in_state->txcnt >= usbp->epc[ep]->in_state->txsize) + return true; + + /* Number of bytes remaining in current transaction.*/ + n = usbp->epc[ep]->in_state->txsize - usbp->epc[ep]->in_state->txcnt; + if (n > usbp->epc[ep]->in_maxsize) + n = usbp->epc[ep]->in_maxsize; + + /* Checks if in the TXFIFO there is enough space to accommodate the + next packet.*/ + if (((usbp->otg->ie[ep].DTXFSTS & DTXFSTS_INEPTFSAV_MASK) * 4) < n) + return false; + +#if STM32_USB_OTGFIFO_FILL_BASEPRI + __set_BASEPRI(CORTEX_PRIO_MASK(STM32_USB_OTGFIFO_FILL_BASEPRI)); +#endif + otg_fifo_write_from_buffer(usbp->otg->FIFO[ep], + usbp->epc[ep]->in_state->txbuf, + n); + usbp->epc[ep]->in_state->txbuf += n; +#if STM32_USB_OTGFIFO_FILL_BASEPRI + __set_BASEPRI(0); +#endif + usbp->epc[ep]->in_state->txcnt += n; + } +} + +/** + * @brief Generic endpoint IN handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static void otg_epin_handler(USBDriver *usbp, usbep_t ep) { + stm32_otg_t *otgp = usbp->otg; + uint32_t epint = otgp->ie[ep].DIEPINT; + + otgp->ie[ep].DIEPINT = epint; + + if (epint & DIEPINT_TOC) { + /* Timeouts not handled yet, not sure how to handle.*/ + } + if ((epint & DIEPINT_XFRC) && (otgp->DIEPMSK & DIEPMSK_XFRCM)) { + /* Transmit transfer complete.*/ + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + if (isp->txsize < isp->totsize) { + /* In case the transaction covered only part of the total transfer + then another transaction is immediately started in order to + cover the remaining.*/ + isp->txsize = isp->totsize - isp->txsize; + isp->txcnt = 0; + osalSysLockFromISR(); + usb_lld_start_in(usbp, ep); + osalSysUnlockFromISR(); + } + else { + /* End on IN transfer.*/ + _usb_isr_invoke_in_cb(usbp, ep); + } + } + if ((epint & DIEPINT_TXFE) && + (otgp->DIEPEMPMSK & DIEPEMPMSK_INEPTXFEM(ep))) { + /* The thread is made ready, it will be scheduled on ISR exit.*/ + osalSysLockFromISR(); + usbp->txpending |= (1 << ep); + otgp->DIEPEMPMSK &= ~(1 << ep); + osalThreadResumeI(&usbp->wait, MSG_OK); + osalSysUnlockFromISR(); + } +} + +/** + * @brief Generic endpoint OUT handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static void otg_epout_handler(USBDriver *usbp, usbep_t ep) { + stm32_otg_t *otgp = usbp->otg; + uint32_t epint = otgp->oe[ep].DOEPINT; + + /* Resets all EP IRQ sources.*/ + otgp->oe[ep].DOEPINT = epint; + + if ((epint & DOEPINT_STUP) && (otgp->DOEPMSK & DOEPMSK_STUPM)) { + /* Setup packets handling, setup packets are handled using a + specific callback.*/ + _usb_isr_invoke_setup_cb(usbp, ep); + + } + if ((epint & DOEPINT_XFRC) && (otgp->DOEPMSK & DOEPMSK_XFRCM)) { + /* Receive transfer complete.*/ + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + + /* A short packet always terminates a transaction.*/ + if (((osp->rxcnt % usbp->epc[ep]->out_maxsize) == 0) && + (osp->rxsize < osp->totsize)) { + /* In case the transaction covered only part of the total transfer + then another transaction is immediately started in order to + cover the remaining.*/ + osp->rxsize = osp->totsize - osp->rxsize; + osp->rxcnt = 0; + osalSysLockFromISR(); + usb_lld_start_out(usbp, ep); + osalSysUnlockFromISR(); + } + else { + /* End on OUT transfer.*/ + _usb_isr_invoke_out_cb(usbp, ep); + } + } +} + +/** + * @brief Isochronous IN transfer failed handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_isoc_in_failed_handler(USBDriver *usbp) { + usbep_t ep; + stm32_otg_t *otgp = usbp->otg; + + for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { + if (((otgp->ie[ep].DIEPCTL & DIEPCTL_EPTYP_MASK) == DIEPCTL_EPTYP_ISO) && + ((otgp->ie[ep].DIEPCTL & DIEPCTL_EPENA) != 0)) { + /* Endpoint enabled -> ISOC IN transfer failed */ + /* Disable endpoint */ + otgp->ie[ep].DIEPCTL |= (DIEPCTL_EPDIS | DIEPCTL_SNAK); + while (otgp->ie[ep].DIEPCTL & DIEPCTL_EPENA) + ; + + /* Flush FIFO */ + otg_txfifo_flush(usbp, ep); + + /* Prepare data for next frame */ + _usb_isr_invoke_in_cb(usbp, ep); + + /* Pump out data for next frame */ + osalSysLockFromISR(); + otgp->DIEPEMPMSK &= ~(1 << ep); + usbp->txpending |= (1 << ep); + osalThreadResumeI(&usbp->wait, MSG_OK); + osalSysUnlockFromISR(); + } + } +} + +/** + * @brief Isochronous OUT transfer failed handler. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void otg_isoc_out_failed_handler(USBDriver *usbp) { + usbep_t ep; + stm32_otg_t *otgp = usbp->otg; + + for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { + if (((otgp->oe[ep].DOEPCTL & DOEPCTL_EPTYP_MASK) == DOEPCTL_EPTYP_ISO) && + ((otgp->oe[ep].DOEPCTL & DOEPCTL_EPENA) != 0)) { + /* Endpoint enabled -> ISOC OUT transfer failed */ + /* Disable endpoint */ + /* FIXME: Core stucks here */ + /*otgp->oe[ep].DOEPCTL |= (DOEPCTL_EPDIS | DOEPCTL_SNAK); + while (otgp->oe[ep].DOEPCTL & DOEPCTL_EPENA) + ;*/ + /* Prepare transfer for next frame */ + _usb_isr_invoke_out_cb(usbp, ep); + } + } +} + +/** + * @brief OTG shared ISR. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +static void usb_lld_serve_interrupt(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + uint32_t sts, src; + + sts = otgp->GINTSTS; + sts &= otgp->GINTMSK; + otgp->GINTSTS = sts; + + /* Reset interrupt handling.*/ + if (sts & GINTSTS_USBRST) { + + /* Resetting pending operations.*/ + usbp->txpending = 0; + + /* Default reset action.*/ + _usb_reset(usbp); + + /* Preventing execution of more handlers, the core has been reset.*/ + return; + } + + /* Wake-up handling.*/ + if (sts & GINTSTS_WKUPINT) { + /* If clocks are gated off, turn them back on (may be the case if + coming out of suspend mode).*/ + if (otgp->PCGCCTL & (PCGCCTL_STPPCLK | PCGCCTL_GATEHCLK)) { + /* Set to zero to un-gate the USB core clocks.*/ + otgp->PCGCCTL &= ~(PCGCCTL_STPPCLK | PCGCCTL_GATEHCLK); + } + + /* Clear the Remote Wake-up Signaling.*/ + otgp->DCTL |= DCTL_RWUSIG; + + _usb_wakeup(usbp); + } + + /* Suspend handling.*/ + if (sts & GINTSTS_USBSUSP) { + + /* Resetting pending operations.*/ + usbp->txpending = 0; + + /* Default suspend action.*/ + _usb_suspend(usbp); + } + + /* Enumeration done.*/ + if (sts & GINTSTS_ENUMDNE) { + /* Full or High speed timing selection.*/ + if ((otgp->DSTS & DSTS_ENUMSPD_MASK) == DSTS_ENUMSPD_HS_480) { + otgp->GUSBCFG = (otgp->GUSBCFG & ~(GUSBCFG_TRDT_MASK)) | + GUSBCFG_TRDT(TRDT_VALUE_HS); + } + else { + otgp->GUSBCFG = (otgp->GUSBCFG & ~(GUSBCFG_TRDT_MASK)) | + GUSBCFG_TRDT(TRDT_VALUE_FS); + } + } + + /* SOF interrupt handling.*/ + if (sts & GINTSTS_SOF) { + _usb_isr_invoke_sof_cb(usbp); + } + + /* Isochronous IN failed handling */ + if (sts & GINTSTS_IISOIXFR) { + otg_isoc_in_failed_handler(usbp); + } + + /* Isochronous OUT failed handling */ + if (sts & GINTSTS_IISOOXFR) { + otg_isoc_out_failed_handler(usbp); + } + + /* RX FIFO not empty handling.*/ + if (sts & GINTSTS_RXFLVL) { + /* The interrupt is masked while the thread has control or it would + be triggered again.*/ + osalSysLockFromISR(); + otgp->GINTMSK &= ~GINTMSK_RXFLVLM; + osalThreadResumeI(&usbp->wait, MSG_OK); + osalSysUnlockFromISR(); + } + + /* IN/OUT endpoints event handling.*/ + src = otgp->DAINT; + if (sts & GINTSTS_IEPINT) { + if (src & (1 << 0)) + otg_epin_handler(usbp, 0); + if (src & (1 << 1)) + otg_epin_handler(usbp, 1); + if (src & (1 << 2)) + otg_epin_handler(usbp, 2); + if (src & (1 << 3)) + otg_epin_handler(usbp, 3); +#if STM32_USB_USE_OTG2 + if (src & (1 << 4)) + otg_epin_handler(usbp, 4); + if (src & (1 << 5)) + otg_epin_handler(usbp, 5); +#endif + } + if (sts & GINTSTS_OEPINT) { + if (src & (1 << 16)) + otg_epout_handler(usbp, 0); + if (src & (1 << 17)) + otg_epout_handler(usbp, 1); + if (src & (1 << 18)) + otg_epout_handler(usbp, 2); + if (src & (1 << 19)) + otg_epout_handler(usbp, 3); +#if STM32_USB_USE_OTG2 + if (src & (1 << 20)) + otg_epout_handler(usbp, 4); + if (src & (1 << 21)) + otg_epout_handler(usbp, 5); +#endif + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_USB_USE_OTG1 || defined(__DOXYGEN__) +#if !defined(STM32_OTG1_HANDLER) +#error "STM32_OTG1_HANDLER not defined" +#endif +/** + * @brief OTG1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_OTG1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + usb_lld_serve_interrupt(&USBD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_USB_USE_OTG2 || defined(__DOXYGEN__) +#if !defined(STM32_OTG2_HANDLER) +#error "STM32_OTG2_HANDLER not defined" +#endif +/** + * @brief OTG2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_OTG2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + usb_lld_serve_interrupt(&USBD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level USB driver initialization. + * + * @notapi + */ +void usb_lld_init(void) { + + /* Driver initialization.*/ +#if STM32_USB_USE_OTG1 + usbObjectInit(&USBD1); + USBD1.wait = NULL; + USBD1.otg = OTG_FS; + USBD1.otgparams = &fsparams; + +#if defined(_CHIBIOS_RT_) + USBD1.tr = NULL; + /* Filling the thread working area here because the function + @p chThdCreateI() does not do it.*/ +#if CH_DBG_FILL_THREADS + { + void *wsp = USBD1.wa_pump; + _thread_memfill((uint8_t *)wsp, + (uint8_t *)wsp + sizeof (USBD1.wa_pump), + CH_DBG_STACK_FILL_VALUE); + } +#endif /* CH_DBG_FILL_THREADS */ +#endif /* defined(_CHIBIOS_RT_) */ +#endif + +#if STM32_USB_USE_OTG2 + usbObjectInit(&USBD2); + USBD2.wait = NULL; + USBD2.otg = OTG_HS; + USBD2.otgparams = &hsparams; + +#if defined(_CHIBIOS_RT_) + USBD2.tr = NULL; + /* Filling the thread working area here because the function + @p chThdCreateI() does not do it.*/ +#if CH_DBG_FILL_THREADS + { + void *wsp = USBD2.wa_pump; + _thread_memfill((uint8_t *)wsp, + (uint8_t *)wsp + sizeof (USBD2.wa_pump), + CH_DBG_STACK_FILL_VALUE); + } +#endif /* CH_DBG_FILL_THREADS */ +#endif /* defined(_CHIBIOS_RT_) */ +#endif +} + +/** + * @brief Configures and activates the USB peripheral. + * @note Starting the OTG cell can be a slow operation carried out with + * interrupts disabled, perform it before starting time-critical + * operations. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_start(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + if (usbp->state == USB_STOP) { + /* Clock activation.*/ + +#if STM32_USB_USE_OTG1 + if (&USBD1 == usbp) { + /* OTG FS clock enable and reset.*/ + rccEnableOTG_FS(false); + rccResetOTG_FS(); + + /* Enables IRQ vector.*/ + nvicEnableVector(STM32_OTG1_NUMBER, STM32_USB_OTG1_IRQ_PRIORITY); + + /* - Forced device mode. + - USB turn-around time = TRDT_VALUE_FS. + - Full Speed 1.1 PHY.*/ + otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_FS) | + GUSBCFG_PHYSEL; + + /* 48MHz 1.1 PHY.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_FS11; + } +#endif + +#if STM32_USB_USE_OTG2 + if (&USBD2 == usbp) { + /* OTG HS clock enable and reset.*/ + rccEnableOTG_HS(false); + rccResetOTG_HS(); + + /* ULPI clock is managed depending on the presence of an external + PHY.*/ +#if defined(BOARD_OTG2_USES_ULPI) + rccEnableOTG_HSULPI(true); +#else + /* Workaround for the problem described here: + http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=1798.*/ + rccDisableOTG_HSULPI(true); +#endif + + /* Enables IRQ vector.*/ + nvicEnableVector(STM32_OTG2_NUMBER, STM32_USB_OTG2_IRQ_PRIORITY); + + /* - Forced device mode. + - USB turn-around time = TRDT_VALUE_HS or TRDT_VALUE_FS.*/ +#if defined(BOARD_OTG2_USES_ULPI) + /* High speed ULPI PHY.*/ + otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_HS) | + GUSBCFG_SRPCAP | GUSBCFG_HNPCAP; +#else + otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_FS) | + GUSBCFG_PHYSEL; +#endif + +#if defined(BOARD_OTG2_USES_ULPI) +#if STM32_USE_USB_OTG2_HS + /* USB 2.0 High Speed PHY in HS mode.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_HS; +#else + /* USB 2.0 High Speed PHY in FS mode.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_HS_FS; +#endif +#else + /* 48MHz 1.1 PHY.*/ + otgp->DCFG = 0x02200000 | DCFG_DSPD_FS11; +#endif + } +#endif + + /* Clearing mask of TXFIFOs to be filled.*/ + usbp->txpending = 0; + + /* PHY enabled.*/ + otgp->PCGCCTL = 0; + + /* VBUS sensing and transceiver enabled.*/ + otgp->GOTGCTL = GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; + +#if defined(BOARD_OTG2_USES_ULPI) +#if STM32_USB_USE_OTG1 + if (&USBD1 == usbp) { + otgp->GCCFG = GCCFG_INIT_VALUE; + } +#endif + +#if STM32_USB_USE_OTG2 + if (&USBD2 == usbp) { + otgp->GCCFG = 0; + } +#endif +#else + otgp->GCCFG = GCCFG_INIT_VALUE; +#endif + + /* Soft core reset.*/ + otg_core_reset(usbp); + + /* Interrupts on TXFIFOs half empty.*/ + otgp->GAHBCFG = 0; + + /* Endpoints re-initialization.*/ + otg_disable_ep(usbp); + + /* Clear all pending Device Interrupts, only the USB Reset interrupt + is required initially.*/ + otgp->DIEPMSK = 0; + otgp->DOEPMSK = 0; + otgp->DAINTMSK = 0; + if (usbp->config->sof_cb == NULL) + otgp->GINTMSK = GINTMSK_ENUMDNEM | GINTMSK_USBRSTM | GINTMSK_USBSUSPM | + GINTMSK_ESUSPM | GINTMSK_SRQM | GINTMSK_WKUM | + GINTMSK_IISOIXFRM | GINTMSK_IISOOXFRM; + else + otgp->GINTMSK = GINTMSK_ENUMDNEM | GINTMSK_USBRSTM | GINTMSK_USBSUSPM | + GINTMSK_ESUSPM | GINTMSK_SRQM | GINTMSK_WKUM | + GINTMSK_IISOIXFRM | GINTMSK_IISOOXFRM | + GINTMSK_SOFM; + + /* Clears all pending IRQs, if any. */ + otgp->GINTSTS = 0xFFFFFFFF; + +#if defined(_CHIBIOS_RT_) + /* Creates the data pump thread. Note, it is created only once.*/ + if (usbp->tr == NULL) { + thread_descriptor_t usbpump_descriptor = { + "usb_pump", + THD_WORKING_AREA_BASE(usbp->wa_pump), + THD_WORKING_AREA_END(usbp->wa_pump), + STM32_USB_OTG_THREAD_PRIO, + usb_lld_pump, + (void *)usbp + }; + + usbp->tr = chThdCreateI(&usbpump_descriptor); + chSchRescheduleS(); + } +#endif + + /* Global interrupts enable.*/ + otgp->GAHBCFG |= GAHBCFG_GINTMSK; + } +} + +/** + * @brief Deactivates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_stop(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + /* If in ready state then disables the USB clock.*/ + if (usbp->state != USB_STOP) { + + /* Disabling all endpoints in case the driver has been stopped while + active.*/ + otg_disable_ep(usbp); + + usbp->txpending = 0; + + otgp->DAINTMSK = 0; + otgp->GAHBCFG = 0; + otgp->GCCFG = 0; + +#if STM32_USB_USE_OTG1 + if (&USBD1 == usbp) { + nvicDisableVector(STM32_OTG1_NUMBER); + rccDisableOTG_FS(false); + } +#endif + +#if STM32_USB_USE_OTG2 + if (&USBD2 == usbp) { + nvicDisableVector(STM32_OTG2_NUMBER); + rccDisableOTG_HS(false); +#if defined(BOARD_OTG2_USES_ULPI) + rccDisableOTG_HSULPI(true) +#endif + } +#endif + } +} + +/** + * @brief USB low level reset routine. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_reset(USBDriver *usbp) { + unsigned i; + stm32_otg_t *otgp = usbp->otg; + + /* Flush the Tx FIFO.*/ + otg_txfifo_flush(usbp, 0); + + /* Endpoint interrupts all disabled and cleared.*/ + otgp->DIEPEMPMSK = 0; + otgp->DAINTMSK = DAINTMSK_OEPM(0) | DAINTMSK_IEPM(0); + + /* All endpoints in NAK mode, interrupts cleared.*/ + for (i = 0; i <= usbp->otgparams->num_endpoints; i++) { + otgp->ie[i].DIEPCTL = DIEPCTL_SNAK; + otgp->oe[i].DOEPCTL = DOEPCTL_SNAK; + otgp->ie[i].DIEPINT = 0xFFFFFFFF; + otgp->oe[i].DOEPINT = 0xFFFFFFFF; + } + + /* Resets the FIFO memory allocator.*/ + otg_ram_reset(usbp); + + /* Receive FIFO size initialization, the address is always zero.*/ + otgp->GRXFSIZ = usbp->otgparams->rx_fifo_size; + otg_rxfifo_flush(usbp); + + /* Resets the device address to zero.*/ + otgp->DCFG = (otgp->DCFG & ~DCFG_DAD_MASK) | DCFG_DAD(0); + + /* Enables also EP-related interrupt sources.*/ + otgp->GINTMSK |= GINTMSK_RXFLVLM | GINTMSK_OEPM | GINTMSK_IEPM; + otgp->DIEPMSK = DIEPMSK_TOCM | DIEPMSK_XFRCM; + otgp->DOEPMSK = DOEPMSK_STUPM | DOEPMSK_XFRCM; + + /* EP0 initialization, it is a special case.*/ + usbp->epc[0] = &ep0config; + otgp->oe[0].DOEPTSIZ = 0; + otgp->oe[0].DOEPCTL = DOEPCTL_SD0PID | DOEPCTL_USBAEP | DOEPCTL_EPTYP_CTRL | + DOEPCTL_MPSIZ(ep0config.out_maxsize); + otgp->ie[0].DIEPTSIZ = 0; + otgp->ie[0].DIEPCTL = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_CTRL | + DIEPCTL_TXFNUM(0) | DIEPCTL_MPSIZ(ep0config.in_maxsize); + otgp->DIEPTXF0 = DIEPTXF_INEPTXFD(ep0config.in_maxsize / 4) | + DIEPTXF_INEPTXSA(otg_ram_alloc(usbp, + ep0config.in_maxsize / 4)); +} + +/** + * @brief Sets the USB address. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_set_address(USBDriver *usbp) { + stm32_otg_t *otgp = usbp->otg; + + otgp->DCFG = (otgp->DCFG & ~DCFG_DAD_MASK) | DCFG_DAD(usbp->address); +} + +/** + * @brief Enables an endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { + uint32_t ctl, fsize; + stm32_otg_t *otgp = usbp->otg; + + /* IN and OUT common parameters.*/ + switch (usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) { + case USB_EP_MODE_TYPE_CTRL: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_CTRL; + break; + case USB_EP_MODE_TYPE_ISOC: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_ISO; + break; + case USB_EP_MODE_TYPE_BULK: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_BULK; + break; + case USB_EP_MODE_TYPE_INTR: + ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_INTR; + break; + default: + return; + } + + /* OUT endpoint activation or deactivation.*/ + otgp->oe[ep].DOEPTSIZ = 0; + if (usbp->epc[ep]->out_state != NULL) { + otgp->oe[ep].DOEPCTL = ctl | DOEPCTL_MPSIZ(usbp->epc[ep]->out_maxsize); + otgp->DAINTMSK |= DAINTMSK_OEPM(ep); + } + else { + otgp->oe[ep].DOEPCTL &= ~DOEPCTL_USBAEP; + otgp->DAINTMSK &= ~DAINTMSK_OEPM(ep); + } + + /* IN endpoint activation or deactivation.*/ + otgp->ie[ep].DIEPTSIZ = 0; + if (usbp->epc[ep]->in_state != NULL) { + /* FIFO allocation for the IN endpoint.*/ + fsize = usbp->epc[ep]->in_maxsize / 4; + if (usbp->epc[ep]->in_multiplier > 1) + fsize *= usbp->epc[ep]->in_multiplier; + otgp->DIEPTXF[ep - 1] = DIEPTXF_INEPTXFD(fsize) | + DIEPTXF_INEPTXSA(otg_ram_alloc(usbp, fsize)); + otg_txfifo_flush(usbp, ep); + + otgp->ie[ep].DIEPCTL = ctl | + DIEPCTL_TXFNUM(ep) | + DIEPCTL_MPSIZ(usbp->epc[ep]->in_maxsize); + otgp->DAINTMSK |= DAINTMSK_IEPM(ep); + } + else { + otgp->DIEPTXF[ep - 1] = 0x02000400; /* Reset value.*/ + otg_txfifo_flush(usbp, ep); + otgp->ie[ep].DIEPCTL &= ~DIEPCTL_USBAEP; + otgp->DAINTMSK &= ~DAINTMSK_IEPM(ep); + } +} + +/** + * @brief Disables all the active endpoints except the endpoint zero. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_disable_endpoints(USBDriver *usbp) { + + /* Resets the FIFO memory allocator.*/ + otg_ram_reset(usbp); + + /* Disabling all endpoints.*/ + otg_disable_ep(usbp); +} + +/** + * @brief Returns the status of an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { + uint32_t ctl; + + (void)usbp; + + ctl = usbp->otg->oe[ep].DOEPCTL; + if (!(ctl & DOEPCTL_USBAEP)) + return EP_STATUS_DISABLED; + if (ctl & DOEPCTL_STALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +} + +/** + * @brief Returns the status of an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { + uint32_t ctl; + + (void)usbp; + + ctl = usbp->otg->ie[ep].DIEPCTL; + if (!(ctl & DIEPCTL_USBAEP)) + return EP_STATUS_DISABLED; + if (ctl & DIEPCTL_STALL) + return EP_STATUS_STALLED; + return EP_STATUS_ACTIVE; +} + +/** + * @brief Reads a setup packet from the dedicated packet buffer. + * @details This function must be invoked in the context of the @p setup_cb + * callback in order to read the received setup packet. + * @pre In order to use this function the endpoint must have been + * initialized as a control endpoint. + * @post The endpoint is ready to accept another packet. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[out] buf buffer where to copy the packet data + * + * @notapi + */ +void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { + + memcpy(buf, usbp->epc[ep]->setup_buf, 8); +} + +/** + * @brief Starts a receive operation on an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { + uint32_t pcnt, rxsize; + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + + /* Transfer initialization.*/ + osp->totsize = osp->rxsize; + if ((ep == 0) && (osp->rxsize > EP0_MAX_OUTSIZE)) + osp->rxsize = EP0_MAX_OUTSIZE; + + /* Transaction size is rounded to a multiple of packet size because the + following requirement in the RM: + "For OUT transfers, the transfer size field in the endpoint's transfer + size register must be a multiple of the maximum packet size of the + endpoint, adjusted to the Word boundary".*/ + pcnt = (osp->rxsize + usbp->epc[ep]->out_maxsize - 1U) / + usbp->epc[ep]->out_maxsize; + rxsize = (pcnt * usbp->epc[ep]->out_maxsize + 3U) & 0xFFFFFFFCU; + + /*Setting up transaction parameters in DOEPTSIZ.*/ + usbp->otg->oe[ep].DOEPTSIZ = DOEPTSIZ_STUPCNT(3) | DOEPTSIZ_PKTCNT(pcnt) | + DOEPTSIZ_XFRSIZ(rxsize); + + /* Special case of isochronous endpoint.*/ + if ((usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { + /* Odd/even bit toggling for isochronous endpoint.*/ + if (usbp->otg->DSTS & DSTS_FNSOF_ODD) + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_SEVNFRM; + else + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_SODDFRM; + } + + /* Starting operation.*/ + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_EPENA | DOEPCTL_CNAK; +} + +/** + * @brief Starts a transmit operation on an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + /* Transfer initialization.*/ + isp->totsize = isp->txsize; + if (isp->txsize == 0) { + /* Special case, sending zero size packet.*/ + usbp->otg->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(1) | DIEPTSIZ_XFRSIZ(0); + } + else { + if ((ep == 0) && (isp->txsize > EP0_MAX_INSIZE)) + isp->txsize = EP0_MAX_INSIZE; + + /* Normal case.*/ + uint32_t pcnt = (isp->txsize + usbp->epc[ep]->in_maxsize - 1) / + usbp->epc[ep]->in_maxsize; + /* TODO: Support more than one packet per frame for isochronous transfers.*/ + usbp->otg->ie[ep].DIEPTSIZ = DIEPTSIZ_MCNT(1) | DIEPTSIZ_PKTCNT(pcnt) | + DIEPTSIZ_XFRSIZ(isp->txsize); + } + + /* Special case of isochronous endpoint.*/ + if ((usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { + /* Odd/even bit toggling.*/ + if (usbp->otg->DSTS & DSTS_FNSOF_ODD) + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_SEVNFRM; + else + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_SODDFRM; + } + + /* Starting operation.*/ + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_EPENA | DIEPCTL_CNAK; + usbp->otg->DIEPEMPMSK |= DIEPEMPMSK_INEPTXFEM(ep); +} + +/** + * @brief Brings an OUT endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { + + usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_STALL; +} + +/** + * @brief Brings an IN endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { + + usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_STALL; +} + +/** + * @brief Brings an OUT endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { + + usbp->otg->oe[ep].DOEPCTL &= ~DOEPCTL_STALL; +} + +/** + * @brief Brings an IN endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { + + usbp->otg->ie[ep].DIEPCTL &= ~DIEPCTL_STALL; +} + +/** + * @brief USB data transfer loop. + * @details This function must be executed by a system thread in order to + * make the USB driver work. + * @note The data copy part of the driver is implemented in this thread + * in order to not perform heavy tasks within interrupt handlers. + * + * @param[in] p pointer to the @p USBDriver object + * + * @special + */ +void usb_lld_pump(void *p) { + USBDriver *usbp = (USBDriver *)p; + stm32_otg_t *otgp = usbp->otg; + + osalSysLock(); + while (true) { + usbep_t ep; + uint32_t epmask; + + /* Nothing to do, going to sleep.*/ + if ((usbp->state == USB_STOP) || + ((usbp->txpending == 0) && !(otgp->GINTSTS & GINTSTS_RXFLVL))) { + otgp->GINTMSK |= GINTMSK_RXFLVLM; + osalThreadSuspendS(&usbp->wait); + } + osalSysUnlock(); + + /* Checks if there are TXFIFOs to be filled.*/ + for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { + + /* Empties the RX FIFO.*/ + while (otgp->GINTSTS & GINTSTS_RXFLVL) { + otg_rxfifo_handler(usbp); + } + + epmask = (1 << ep); + if (usbp->txpending & epmask) { + bool done; + + osalSysLock(); + /* USB interrupts are globally *suspended* because the peripheral + does not allow any interference during the TX FIFO filling + operation. + Synopsys document: DesignWare Cores USB 2.0 Hi-Speed On-The-Go (OTG) + "The application has to finish writing one complete packet before + switching to a different channel/endpoint FIFO. Violating this + rule results in an error.".*/ + otgp->GAHBCFG &= ~GAHBCFG_GINTMSK; + usbp->txpending &= ~epmask; + osalSysUnlock(); + + done = otg_txfifo_handler(usbp, ep); + + osalSysLock(); + otgp->GAHBCFG |= GAHBCFG_GINTMSK; + if (!done) + otgp->DIEPEMPMSK |= epmask; + osalSysUnlock(); + } + } + osalSysLock(); + } +} + +#endif /* HAL_USE_USB */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.h b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.h new file mode 100644 index 000000000..8b3ee8957 --- /dev/null +++ b/os/hal/ports/STM32/LLD/OTGv1/hal_usb_lld.h @@ -0,0 +1,556 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/OTGv1/usb_lld.h + * @brief STM32 USB subsystem low level driver header. + * + * @addtogroup USB + * @{ + */ + +#ifndef _USB_LLD_H_ +#define _USB_LLD_H_ + +#if HAL_USE_USB || defined(__DOXYGEN__) + +#include "stm32_otg.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Status stage handling method. + */ +#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW + +/** + * @brief The address can be changed immediately upon packet reception. + */ +#define USB_SET_ADDRESS_MODE USB_EARLY_SET_ADDRESS + +/** + * @brief Method for set address acknowledge. + */ +#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief OTG1 driver enable switch. + * @details If set to @p TRUE the support for OTG_FS is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_USB_USE_OTG1) || defined(__DOXYGEN__) +#define STM32_USB_USE_OTG1 FALSE +#endif + +/** + * @brief OTG2 driver enable switch. + * @details If set to @p TRUE the support for OTG_HS is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_USB_USE_OTG2) || defined(__DOXYGEN__) +#define STM32_USB_USE_OTG2 FALSE +#endif + +/** + * @brief OTG1 interrupt priority level setting. + */ +#if !defined(STM32_USB_OTG1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_OTG1_IRQ_PRIORITY 14 +#endif + +/** + * @brief OTG2 interrupt priority level setting. + */ +#if !defined(STM32_USB_OTG2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_OTG2_IRQ_PRIORITY 14 +#endif + +/** + * @brief OTG1 RX shared FIFO size. + * @note Must be a multiple of 4. + */ +#if !defined(STM32_USB_OTG1_RX_FIFO_SIZE) || defined(__DOXYGEN__) +#define STM32_USB_OTG1_RX_FIFO_SIZE 512 +#endif + +/** + * @brief OTG2 RX shared FIFO size. + * @note Must be a multiple of 4. + */ +#if !defined(STM32_USB_OTG2_RX_FIFO_SIZE) || defined(__DOXYGEN__) +#define STM32_USB_OTG2_RX_FIFO_SIZE 1024 +#endif + +/** + * @brief Enables HS mode on OTG2 else FS mode. + * @note The default is @p TRUE. + * @note Has effect only if @p BOARD_OTG2_USES_ULPI is defined. + */ +#if !defined(STM32_USE_USB_OTG2_HS) || defined(__DOXYGEN__) +#define STM32_USE_USB_OTG2_HS TRUE +#endif + +/** + * @brief Dedicated data pump threads priority. + */ +#if !defined(STM32_USB_OTG_THREAD_PRIO) || defined(__DOXYGEN__) +#define STM32_USB_OTG_THREAD_PRIO LOWPRIO +#endif + +/** + * @brief Dedicated data pump threads stack size. + */ +#if !defined(STM32_USB_OTG_THREAD_STACK_SIZE) || defined(__DOXYGEN__) +#define STM32_USB_OTG_THREAD_STACK_SIZE 128 +#endif + +/** + * @brief Exception priority level during TXFIFOs operations. + * @note Because an undocumented silicon behavior the operation of + * copying a packet into a TXFIFO must not be interrupted by + * any other operation on the OTG peripheral. + * This parameter represents the priority mask during copy + * operations. The default value only allows to call USB + * functions from callbacks invoked from USB ISR handlers. + * If you need to invoke USB functions from other handlers + * then raise this priority mast to the same level of the + * handler you need to use. + * @note The value zero means disabled, when disabled calling USB + * functions is only safe from thread level or from USB + * callbacks. + */ +#if !defined(STM32_USB_OTGFIFO_FILL_BASEPRI) || defined(__DOXYGEN__) +#define STM32_USB_OTGFIFO_FILL_BASEPRI 0 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/** + * @brief Maximum endpoint address. + */ +#if !STM32_USB_USE_OTG2 || defined(__DOXYGEN__) +#define USB_MAX_ENDPOINTS 3 +#else +#define USB_MAX_ENDPOINTS 5 +#endif + +#if STM32_USB_USE_OTG1 && !STM32_HAS_OTG1 +#error "OTG1 not present in the selected device" +#endif + +#if STM32_USB_USE_OTG2 && !STM32_HAS_OTG2 +#error "OTG2 not present in the selected device" +#endif + +#if !STM32_USB_USE_OTG1 && !STM32_USB_USE_OTG2 +#error "USB driver activated but no USB peripheral assigned" +#endif + +#if STM32_USB_USE_OTG1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_OTG1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to OTG1" +#endif + +#if STM32_USB_USE_OTG2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_OTG2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to OTG2" +#endif + +#if (STM32_USB_OTG1_RX_FIFO_SIZE & 3) != 0 +#error "OTG1 RX FIFO size must be a multiple of 4" +#endif + +#if (STM32_USB_OTG2_RX_FIFO_SIZE & 3) != 0 +#error "OTG2 RX FIFO size must be a multiple of 4" +#endif + +#if defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) +#define STM32_USBCLK STM32_PLL48CLK +#elif defined(STM32F10X_CL) +#define STM32_USBCLK STM32_OTGFSCLK +#elif defined(STM32L4XX) +#define STM32_USBCLK STM32_48CLK +#else +#error "unsupported STM32 platform for OTG functionality" +#endif + +#if STM32_USBCLK != 48000000 +#error "the USB OTG driver requires a 48MHz clock" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Peripheral-specific parameters block. + */ +typedef struct { + uint32_t rx_fifo_size; + uint32_t otg_ram_size; + uint32_t num_endpoints; +} stm32_otg_params_t; + +/** + * @brief Type of an IN endpoint state structure. + */ +typedef struct { + /** + * @brief Requested transmit transfer size. + */ + size_t txsize; + /** + * @brief Transmitted bytes so far. + */ + size_t txcnt; + /** + * @brief Pointer to the transmission linear buffer. + */ + const uint8_t *txbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Total transmit transfer size. + */ + size_t totsize; +} USBInEndpointState; + +/** + * @brief Type of an OUT endpoint state structure. + */ +typedef struct { + /** + * @brief Requested receive transfer size. + */ + size_t rxsize; + /** + * @brief Received bytes so far. + */ + size_t rxcnt; + /** + * @brief Pointer to the receive linear buffer. + */ + uint8_t *rxbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Total receive transfer size. + */ + size_t totsize; +} USBOutEndpointState; + +/** + * @brief Type of an USB endpoint configuration structure. + * @note Platform specific restrictions may apply to endpoints. + */ +typedef struct { + /** + * @brief Type and mode of the endpoint. + */ + uint32_t ep_mode; + /** + * @brief Setup packet notification callback. + * @details This callback is invoked when a setup packet has been + * received. + * @post The application must immediately call @p usbReadPacket() in + * order to access the received packet. + * @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL + * endpoints, it should be set to @p NULL for other endpoint + * types. + */ + usbepcallback_t setup_cb; + /** + * @brief IN endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t in_cb; + /** + * @brief OUT endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t out_cb; + /** + * @brief IN endpoint maximum packet size. + * @details This field must be set to zero if the IN endpoint is not used. + */ + uint16_t in_maxsize; + /** + * @brief OUT endpoint maximum packet size. + * @details This field must be set to zero if the OUT endpoint is not used. + */ + uint16_t out_maxsize; + /** + * @brief @p USBEndpointState associated to the IN endpoint. + * @details This field must be set to @p NULL if the IN endpoint is not + * used. + */ + USBInEndpointState *in_state; + /** + * @brief @p USBEndpointState associated to the OUT endpoint. + * @details This field must be set to @p NULL if the OUT endpoint is not + * used. + */ + USBOutEndpointState *out_state; + /* End of the mandatory fields.*/ + /** + * @brief Determines the space allocated for the TXFIFO as multiples of + * the packet size (@p in_maxsize). Note that zero is interpreted + * as one for simplicity and robustness. + */ + uint16_t in_multiplier; + /** + * @brief Pointer to a buffer for setup packets. + * @details Setup packets require a dedicated 8-bytes buffer, set this + * field to @p NULL for non-control endpoints. + */ + uint8_t *setup_buf; +} USBEndpointConfig; + +/** + * @brief Type of an USB driver configuration structure. + */ +typedef struct { + /** + * @brief USB events callback. + * @details This callback is invoked when an USB driver event is registered. + */ + usbeventcb_t event_cb; + /** + * @brief Device GET_DESCRIPTOR request callback. + * @note This callback is mandatory and cannot be set to @p NULL. + */ + usbgetdescriptor_t get_descriptor_cb; + /** + * @brief Requests hook callback. + * @details This hook allows to be notified of standard requests or to + * handle non standard requests. + */ + usbreqhandler_t requests_hook_cb; + /** + * @brief Start Of Frame callback. + */ + usbcallback_t sof_cb; + /* End of the mandatory fields.*/ +} USBConfig; + +/** + * @brief Structure representing an USB driver. + */ +struct USBDriver { + /** + * @brief Driver state. + */ + usbstate_t state; + /** + * @brief Current configuration data. + */ + const USBConfig *config; + /** + * @brief Bit map of the transmitting IN endpoints. + */ + uint16_t transmitting; + /** + * @brief Bit map of the receiving OUT endpoints. + */ + uint16_t receiving; + /** + * @brief Active endpoints configurations. + */ + const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an IN endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *in_params[USB_MAX_ENDPOINTS]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an OUT endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *out_params[USB_MAX_ENDPOINTS]; + /** + * @brief Endpoint 0 state. + */ + usbep0state_t ep0state; + /** + * @brief Next position in the buffer to be transferred through endpoint 0. + */ + uint8_t *ep0next; + /** + * @brief Number of bytes yet to be transferred through endpoint 0. + */ + size_t ep0n; + /** + * @brief Endpoint 0 end transaction callback. + */ + usbcallback_t ep0endcb; + /** + * @brief Setup packet buffer. + */ + uint8_t setup[8]; + /** + * @brief Current USB device status. + */ + uint16_t status; + /** + * @brief Assigned USB address. + */ + uint8_t address; + /** + * @brief Current USB device configuration. + */ + uint8_t configuration; +#if defined(USB_DRIVER_EXT_FIELDS) + USB_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the OTG peripheral associated to this driver. + */ + stm32_otg_t *otg; + /** + * @brief Peripheral-specific parameters. + */ + const stm32_otg_params_t *otgparams; + /** + * @brief Pointer to the next address in the packet memory. + */ + uint32_t pmnext; + /** + * @brief Mask of TXFIFOs to be filled by the pump thread. + */ + uint32_t txpending; + /** + * @brief Pointer to the thread when it is sleeping or @p NULL. + */ + thread_reference_t wait; +#if defined(_CHIBIOS_RT_) + /** + * @brief Pointer to the thread. + */ + thread_reference_t tr; + /** + * @brief Working area for the dedicated data pump thread; + */ + THD_WORKING_AREA(wa_pump, STM32_USB_OTG_THREAD_STACK_SIZE); +#endif +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the exact size of a receive transaction. + * @details The received size can be different from the size specified in + * @p usbStartReceiveI() because the last packet could have a size + * different from the expected one. + * @pre The OUT endpoint must have been configured in transaction mode + * in order to use this function. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return Received data size. + * + * @notapi + */ +#define usb_lld_get_transaction_size(usbp, ep) \ + ((usbp)->epc[ep]->out_state->rxcnt) + +/** + * @brief Connects the USB device. + * + * @api + */ +#if defined(STM32F7XX) || defined(__DOXYGEN__) +#define usb_lld_connect_bus(usbp) ((usbp)->otg->DCTL &= ~DCTL_SDIS) +#else +#define usb_lld_connect_bus(usbp) ((usbp)->otg->GCCFG |= GCCFG_VBUSBSEN) +#endif + +/** + * @brief Disconnect the USB device. + * + * @api + */ +#if defined(STM32F7XX) || defined(__DOXYGEN__) +#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->DCTL |= DCTL_SDIS) +#else +#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->GCCFG &= ~GCCFG_VBUSBSEN) +#endif + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_USB_USE_OTG1 && !defined(__DOXYGEN__) +extern USBDriver USBD1; +#endif + +#if STM32_USB_USE_OTG2 && !defined(__DOXYGEN__) +extern USBDriver USBD2; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void usb_lld_init(void); + void usb_lld_start(USBDriver *usbp); + void usb_lld_stop(USBDriver *usbp); + void usb_lld_reset(USBDriver *usbp); + void usb_lld_set_address(USBDriver *usbp); + void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep); + void usb_lld_disable_endpoints(USBDriver *usbp); + usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep); + usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep); + void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf); + void usb_lld_start_out(USBDriver *usbp, usbep_t ep); + void usb_lld_start_in(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_out(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_in(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_out(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_in(USBDriver *usbp, usbep_t ep); + void usb_lld_pump(void *p); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB */ + +#endif /* _USB_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/OTGv1/usb_lld.c b/os/hal/ports/STM32/LLD/OTGv1/usb_lld.c deleted file mode 100644 index 1c4878b25..000000000 --- a/os/hal/ports/STM32/LLD/OTGv1/usb_lld.c +++ /dev/null @@ -1,1341 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/OTGv1/usb_lld.c - * @brief STM32 USB subsystem low level driver source. - * - * @addtogroup USB - * @{ - */ - -#include - -#include "hal.h" - -#if HAL_USE_USB || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define TRDT_VALUE_FS 5 -#define TRDT_VALUE_HS 9 - -#define EP0_MAX_INSIZE 64 -#define EP0_MAX_OUTSIZE 64 - -#if defined(STM32F7XX) -#define GCCFG_INIT_VALUE GCCFG_PWRDWN -#else -#if defined(BOARD_OTG_NOVBUSSENS) -#define GCCFG_INIT_VALUE (GCCFG_NOVBUSSENS | GCCFG_VBUSASEN | \ - GCCFG_VBUSBSEN | GCCFG_PWRDWN) -#else -#define GCCFG_INIT_VALUE (GCCFG_VBUSASEN | GCCFG_VBUSBSEN | \ - GCCFG_PWRDWN) -#endif -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief OTG_FS driver identifier.*/ -#if STM32_USB_USE_OTG1 || defined(__DOXYGEN__) -USBDriver USBD1; -#endif - -/** @brief OTG_HS driver identifier.*/ -#if STM32_USB_USE_OTG2 || defined(__DOXYGEN__) -USBDriver USBD2; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/** - * @brief EP0 state. - * @note It is an union because IN and OUT endpoints are never used at the - * same time for EP0. - */ -static union { - /** - * @brief IN EP0 state. - */ - USBInEndpointState in; - /** - * @brief OUT EP0 state. - */ - USBOutEndpointState out; -} ep0_state; - -/** - * @brief Buffer for the EP0 setup packets. - */ -static uint8_t ep0setup_buffer[8]; - -/** - * @brief EP0 initialization structure. - */ -static const USBEndpointConfig ep0config = { - USB_EP_MODE_TYPE_CTRL, - _usb_ep0setup, - _usb_ep0in, - _usb_ep0out, - 0x40, - 0x40, - &ep0_state.in, - &ep0_state.out, - 1, - ep0setup_buffer -}; - -#if STM32_USB_USE_OTG1 -static const stm32_otg_params_t fsparams = { - STM32_USB_OTG1_RX_FIFO_SIZE / 4, - STM32_OTG1_FIFO_MEM_SIZE, - STM32_OTG1_ENDOPOINTS_NUMBER -}; -#endif - -#if STM32_USB_USE_OTG2 -static const stm32_otg_params_t hsparams = { - STM32_USB_OTG2_RX_FIFO_SIZE / 4, - STM32_OTG2_FIFO_MEM_SIZE, - STM32_OTG2_ENDOPOINTS_NUMBER -}; -#endif - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static void otg_core_reset(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - - osalSysPolledDelayX(32); - - /* Core reset and delay of at least 3 PHY cycles.*/ - otgp->GRSTCTL = GRSTCTL_CSRST; - while ((otgp->GRSTCTL & GRSTCTL_CSRST) != 0) - ; - - osalSysPolledDelayX(18); - - /* Wait AHB idle condition.*/ - while ((otgp->GRSTCTL & GRSTCTL_AHBIDL) == 0) - ; -} - -static void otg_disable_ep(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - unsigned i; - - for (i = 0; i <= usbp->otgparams->num_endpoints; i++) { - otgp->ie[i].DIEPCTL = 0; - otgp->ie[i].DIEPTSIZ = 0; - otgp->ie[i].DIEPINT = 0xFFFFFFFF; - - otgp->oe[i].DOEPCTL = 0; - otgp->oe[i].DOEPTSIZ = 0; - otgp->oe[i].DOEPINT = 0xFFFFFFFF; - } - otgp->DAINTMSK = DAINTMSK_OEPM(0) | DAINTMSK_IEPM(0); -} - -static void otg_rxfifo_flush(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - - otgp->GRSTCTL = GRSTCTL_RXFFLSH; - while ((otgp->GRSTCTL & GRSTCTL_RXFFLSH) != 0) - ; - /* Wait for 3 PHY Clocks.*/ - osalSysPolledDelayX(18); -} - -static void otg_txfifo_flush(USBDriver *usbp, uint32_t fifo) { - stm32_otg_t *otgp = usbp->otg; - - otgp->GRSTCTL = GRSTCTL_TXFNUM(fifo) | GRSTCTL_TXFFLSH; - while ((otgp->GRSTCTL & GRSTCTL_TXFFLSH) != 0) - ; - /* Wait for 3 PHY Clocks.*/ - osalSysPolledDelayX(18); -} - -/** - * @brief Resets the FIFO RAM memory allocator. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -static void otg_ram_reset(USBDriver *usbp) { - - usbp->pmnext = usbp->otgparams->rx_fifo_size; -} - -/** - * @brief Allocates a block from the FIFO RAM memory. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] size size of the packet buffer to allocate in words - * - * @notapi - */ -static uint32_t otg_ram_alloc(USBDriver *usbp, size_t size) { - uint32_t next; - - next = usbp->pmnext; - usbp->pmnext += size; - osalDbgAssert(usbp->pmnext <= usbp->otgparams->otg_ram_size, - "OTG FIFO memory overflow"); - return next; -} - -/** - * @brief Writes to a TX FIFO. - * - * @param[in] fifop pointer to the FIFO register - * @param[in] buf buffer where to copy the endpoint data - * @param[in] n maximum number of bytes to copy - * - * @notapi - */ -static void otg_fifo_write_from_buffer(volatile uint32_t *fifop, - const uint8_t *buf, - size_t n) { - - osalDbgAssert(n > 0, "is zero"); - - while (true) { - *fifop = *((uint32_t *)buf); - if (n <= 4) { - break; - } - n -= 4; - buf += 4; - } -} - -/** - * @brief Reads a packet from the RXFIFO. - * - * @param[in] fifop pointer to the FIFO register - * @param[out] buf buffer where to copy the endpoint data - * @param[in] n number of bytes to pull from the FIFO - * @param[in] max number of bytes to copy into the buffer - * - * @notapi - */ -static void otg_fifo_read_to_buffer(volatile uint32_t *fifop, - uint8_t *buf, - size_t n, - size_t max) { - uint32_t w = 0; - size_t i = 0; - - while (i < n) { - if ((i & 3) == 0){ - w = *fifop; - } - if (i < max) { - *buf++ = (uint8_t)w; - w >>= 8; - } - i++; - } -} - -/** - * @brief Incoming packets handler. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -static void otg_rxfifo_handler(USBDriver *usbp) { - uint32_t sts, cnt, ep; - - sts = usbp->otg->GRXSTSP; - switch (sts & GRXSTSP_PKTSTS_MASK) { - case GRXSTSP_SETUP_COMP: - break; - case GRXSTSP_SETUP_DATA: - cnt = (sts & GRXSTSP_BCNT_MASK) >> GRXSTSP_BCNT_OFF; - ep = (sts & GRXSTSP_EPNUM_MASK) >> GRXSTSP_EPNUM_OFF; - otg_fifo_read_to_buffer(usbp->otg->FIFO[0], usbp->epc[ep]->setup_buf, - cnt, 8); - break; - case GRXSTSP_OUT_DATA: - cnt = (sts & GRXSTSP_BCNT_MASK) >> GRXSTSP_BCNT_OFF; - ep = (sts & GRXSTSP_EPNUM_MASK) >> GRXSTSP_EPNUM_OFF; - otg_fifo_read_to_buffer(usbp->otg->FIFO[0], - usbp->epc[ep]->out_state->rxbuf, - cnt, - usbp->epc[ep]->out_state->rxsize - - usbp->epc[ep]->out_state->rxcnt); - usbp->epc[ep]->out_state->rxbuf += cnt; - usbp->epc[ep]->out_state->rxcnt += cnt; - break; - case GRXSTSP_OUT_GLOBAL_NAK: - case GRXSTSP_OUT_COMP: - default: - ; - } -} - -/** - * @brief Outgoing packets handler. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -static bool otg_txfifo_handler(USBDriver *usbp, usbep_t ep) { - - /* The TXFIFO is filled until there is space and data to be transmitted.*/ - while (true) { - uint32_t n; - - /* Transaction end condition.*/ - if (usbp->epc[ep]->in_state->txcnt >= usbp->epc[ep]->in_state->txsize) - return true; - - /* Number of bytes remaining in current transaction.*/ - n = usbp->epc[ep]->in_state->txsize - usbp->epc[ep]->in_state->txcnt; - if (n > usbp->epc[ep]->in_maxsize) - n = usbp->epc[ep]->in_maxsize; - - /* Checks if in the TXFIFO there is enough space to accommodate the - next packet.*/ - if (((usbp->otg->ie[ep].DTXFSTS & DTXFSTS_INEPTFSAV_MASK) * 4) < n) - return false; - -#if STM32_USB_OTGFIFO_FILL_BASEPRI - __set_BASEPRI(CORTEX_PRIO_MASK(STM32_USB_OTGFIFO_FILL_BASEPRI)); -#endif - otg_fifo_write_from_buffer(usbp->otg->FIFO[ep], - usbp->epc[ep]->in_state->txbuf, - n); - usbp->epc[ep]->in_state->txbuf += n; -#if STM32_USB_OTGFIFO_FILL_BASEPRI - __set_BASEPRI(0); -#endif - usbp->epc[ep]->in_state->txcnt += n; - } -} - -/** - * @brief Generic endpoint IN handler. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -static void otg_epin_handler(USBDriver *usbp, usbep_t ep) { - stm32_otg_t *otgp = usbp->otg; - uint32_t epint = otgp->ie[ep].DIEPINT; - - otgp->ie[ep].DIEPINT = epint; - - if (epint & DIEPINT_TOC) { - /* Timeouts not handled yet, not sure how to handle.*/ - } - if ((epint & DIEPINT_XFRC) && (otgp->DIEPMSK & DIEPMSK_XFRCM)) { - /* Transmit transfer complete.*/ - USBInEndpointState *isp = usbp->epc[ep]->in_state; - - if (isp->txsize < isp->totsize) { - /* In case the transaction covered only part of the total transfer - then another transaction is immediately started in order to - cover the remaining.*/ - isp->txsize = isp->totsize - isp->txsize; - isp->txcnt = 0; - osalSysLockFromISR(); - usb_lld_start_in(usbp, ep); - osalSysUnlockFromISR(); - } - else { - /* End on IN transfer.*/ - _usb_isr_invoke_in_cb(usbp, ep); - } - } - if ((epint & DIEPINT_TXFE) && - (otgp->DIEPEMPMSK & DIEPEMPMSK_INEPTXFEM(ep))) { - /* The thread is made ready, it will be scheduled on ISR exit.*/ - osalSysLockFromISR(); - usbp->txpending |= (1 << ep); - otgp->DIEPEMPMSK &= ~(1 << ep); - osalThreadResumeI(&usbp->wait, MSG_OK); - osalSysUnlockFromISR(); - } -} - -/** - * @brief Generic endpoint OUT handler. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -static void otg_epout_handler(USBDriver *usbp, usbep_t ep) { - stm32_otg_t *otgp = usbp->otg; - uint32_t epint = otgp->oe[ep].DOEPINT; - - /* Resets all EP IRQ sources.*/ - otgp->oe[ep].DOEPINT = epint; - - if ((epint & DOEPINT_STUP) && (otgp->DOEPMSK & DOEPMSK_STUPM)) { - /* Setup packets handling, setup packets are handled using a - specific callback.*/ - _usb_isr_invoke_setup_cb(usbp, ep); - - } - if ((epint & DOEPINT_XFRC) && (otgp->DOEPMSK & DOEPMSK_XFRCM)) { - /* Receive transfer complete.*/ - USBOutEndpointState *osp = usbp->epc[ep]->out_state; - - /* A short packet always terminates a transaction.*/ - if (((osp->rxcnt % usbp->epc[ep]->out_maxsize) == 0) && - (osp->rxsize < osp->totsize)) { - /* In case the transaction covered only part of the total transfer - then another transaction is immediately started in order to - cover the remaining.*/ - osp->rxsize = osp->totsize - osp->rxsize; - osp->rxcnt = 0; - osalSysLockFromISR(); - usb_lld_start_out(usbp, ep); - osalSysUnlockFromISR(); - } - else { - /* End on OUT transfer.*/ - _usb_isr_invoke_out_cb(usbp, ep); - } - } -} - -/** - * @brief Isochronous IN transfer failed handler. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -static void otg_isoc_in_failed_handler(USBDriver *usbp) { - usbep_t ep; - stm32_otg_t *otgp = usbp->otg; - - for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { - if (((otgp->ie[ep].DIEPCTL & DIEPCTL_EPTYP_MASK) == DIEPCTL_EPTYP_ISO) && - ((otgp->ie[ep].DIEPCTL & DIEPCTL_EPENA) != 0)) { - /* Endpoint enabled -> ISOC IN transfer failed */ - /* Disable endpoint */ - otgp->ie[ep].DIEPCTL |= (DIEPCTL_EPDIS | DIEPCTL_SNAK); - while (otgp->ie[ep].DIEPCTL & DIEPCTL_EPENA) - ; - - /* Flush FIFO */ - otg_txfifo_flush(usbp, ep); - - /* Prepare data for next frame */ - _usb_isr_invoke_in_cb(usbp, ep); - - /* Pump out data for next frame */ - osalSysLockFromISR(); - otgp->DIEPEMPMSK &= ~(1 << ep); - usbp->txpending |= (1 << ep); - osalThreadResumeI(&usbp->wait, MSG_OK); - osalSysUnlockFromISR(); - } - } -} - -/** - * @brief Isochronous OUT transfer failed handler. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -static void otg_isoc_out_failed_handler(USBDriver *usbp) { - usbep_t ep; - stm32_otg_t *otgp = usbp->otg; - - for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { - if (((otgp->oe[ep].DOEPCTL & DOEPCTL_EPTYP_MASK) == DOEPCTL_EPTYP_ISO) && - ((otgp->oe[ep].DOEPCTL & DOEPCTL_EPENA) != 0)) { - /* Endpoint enabled -> ISOC OUT transfer failed */ - /* Disable endpoint */ - /* FIXME: Core stucks here */ - /*otgp->oe[ep].DOEPCTL |= (DOEPCTL_EPDIS | DOEPCTL_SNAK); - while (otgp->oe[ep].DOEPCTL & DOEPCTL_EPENA) - ;*/ - /* Prepare transfer for next frame */ - _usb_isr_invoke_out_cb(usbp, ep); - } - } -} - -/** - * @brief OTG shared ISR. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -static void usb_lld_serve_interrupt(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - uint32_t sts, src; - - sts = otgp->GINTSTS; - sts &= otgp->GINTMSK; - otgp->GINTSTS = sts; - - /* Reset interrupt handling.*/ - if (sts & GINTSTS_USBRST) { - - /* Resetting pending operations.*/ - usbp->txpending = 0; - - /* Default reset action.*/ - _usb_reset(usbp); - - /* Preventing execution of more handlers, the core has been reset.*/ - return; - } - - /* Wake-up handling.*/ - if (sts & GINTSTS_WKUPINT) { - /* If clocks are gated off, turn them back on (may be the case if - coming out of suspend mode).*/ - if (otgp->PCGCCTL & (PCGCCTL_STPPCLK | PCGCCTL_GATEHCLK)) { - /* Set to zero to un-gate the USB core clocks.*/ - otgp->PCGCCTL &= ~(PCGCCTL_STPPCLK | PCGCCTL_GATEHCLK); - } - - /* Clear the Remote Wake-up Signaling.*/ - otgp->DCTL |= DCTL_RWUSIG; - - _usb_wakeup(usbp); - } - - /* Suspend handling.*/ - if (sts & GINTSTS_USBSUSP) { - - /* Resetting pending operations.*/ - usbp->txpending = 0; - - /* Default suspend action.*/ - _usb_suspend(usbp); - } - - /* Enumeration done.*/ - if (sts & GINTSTS_ENUMDNE) { - /* Full or High speed timing selection.*/ - if ((otgp->DSTS & DSTS_ENUMSPD_MASK) == DSTS_ENUMSPD_HS_480) { - otgp->GUSBCFG = (otgp->GUSBCFG & ~(GUSBCFG_TRDT_MASK)) | - GUSBCFG_TRDT(TRDT_VALUE_HS); - } - else { - otgp->GUSBCFG = (otgp->GUSBCFG & ~(GUSBCFG_TRDT_MASK)) | - GUSBCFG_TRDT(TRDT_VALUE_FS); - } - } - - /* SOF interrupt handling.*/ - if (sts & GINTSTS_SOF) { - _usb_isr_invoke_sof_cb(usbp); - } - - /* Isochronous IN failed handling */ - if (sts & GINTSTS_IISOIXFR) { - otg_isoc_in_failed_handler(usbp); - } - - /* Isochronous OUT failed handling */ - if (sts & GINTSTS_IISOOXFR) { - otg_isoc_out_failed_handler(usbp); - } - - /* RX FIFO not empty handling.*/ - if (sts & GINTSTS_RXFLVL) { - /* The interrupt is masked while the thread has control or it would - be triggered again.*/ - osalSysLockFromISR(); - otgp->GINTMSK &= ~GINTMSK_RXFLVLM; - osalThreadResumeI(&usbp->wait, MSG_OK); - osalSysUnlockFromISR(); - } - - /* IN/OUT endpoints event handling.*/ - src = otgp->DAINT; - if (sts & GINTSTS_IEPINT) { - if (src & (1 << 0)) - otg_epin_handler(usbp, 0); - if (src & (1 << 1)) - otg_epin_handler(usbp, 1); - if (src & (1 << 2)) - otg_epin_handler(usbp, 2); - if (src & (1 << 3)) - otg_epin_handler(usbp, 3); -#if STM32_USB_USE_OTG2 - if (src & (1 << 4)) - otg_epin_handler(usbp, 4); - if (src & (1 << 5)) - otg_epin_handler(usbp, 5); -#endif - } - if (sts & GINTSTS_OEPINT) { - if (src & (1 << 16)) - otg_epout_handler(usbp, 0); - if (src & (1 << 17)) - otg_epout_handler(usbp, 1); - if (src & (1 << 18)) - otg_epout_handler(usbp, 2); - if (src & (1 << 19)) - otg_epout_handler(usbp, 3); -#if STM32_USB_USE_OTG2 - if (src & (1 << 20)) - otg_epout_handler(usbp, 4); - if (src & (1 << 21)) - otg_epout_handler(usbp, 5); -#endif - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_USB_USE_OTG1 || defined(__DOXYGEN__) -#if !defined(STM32_OTG1_HANDLER) -#error "STM32_OTG1_HANDLER not defined" -#endif -/** - * @brief OTG1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_OTG1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - usb_lld_serve_interrupt(&USBD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_USB_USE_OTG2 || defined(__DOXYGEN__) -#if !defined(STM32_OTG2_HANDLER) -#error "STM32_OTG2_HANDLER not defined" -#endif -/** - * @brief OTG2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_OTG2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - usb_lld_serve_interrupt(&USBD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level USB driver initialization. - * - * @notapi - */ -void usb_lld_init(void) { - - /* Driver initialization.*/ -#if STM32_USB_USE_OTG1 - usbObjectInit(&USBD1); - USBD1.wait = NULL; - USBD1.otg = OTG_FS; - USBD1.otgparams = &fsparams; - -#if defined(_CHIBIOS_RT_) - USBD1.tr = NULL; - /* Filling the thread working area here because the function - @p chThdCreateI() does not do it.*/ -#if CH_DBG_FILL_THREADS - { - void *wsp = USBD1.wa_pump; - _thread_memfill((uint8_t *)wsp, - (uint8_t *)wsp + sizeof (USBD1.wa_pump), - CH_DBG_STACK_FILL_VALUE); - } -#endif /* CH_DBG_FILL_THREADS */ -#endif /* defined(_CHIBIOS_RT_) */ -#endif - -#if STM32_USB_USE_OTG2 - usbObjectInit(&USBD2); - USBD2.wait = NULL; - USBD2.otg = OTG_HS; - USBD2.otgparams = &hsparams; - -#if defined(_CHIBIOS_RT_) - USBD2.tr = NULL; - /* Filling the thread working area here because the function - @p chThdCreateI() does not do it.*/ -#if CH_DBG_FILL_THREADS - { - void *wsp = USBD2.wa_pump; - _thread_memfill((uint8_t *)wsp, - (uint8_t *)wsp + sizeof (USBD2.wa_pump), - CH_DBG_STACK_FILL_VALUE); - } -#endif /* CH_DBG_FILL_THREADS */ -#endif /* defined(_CHIBIOS_RT_) */ -#endif -} - -/** - * @brief Configures and activates the USB peripheral. - * @note Starting the OTG cell can be a slow operation carried out with - * interrupts disabled, perform it before starting time-critical - * operations. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_start(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - - if (usbp->state == USB_STOP) { - /* Clock activation.*/ - -#if STM32_USB_USE_OTG1 - if (&USBD1 == usbp) { - /* OTG FS clock enable and reset.*/ - rccEnableOTG_FS(false); - rccResetOTG_FS(); - - /* Enables IRQ vector.*/ - nvicEnableVector(STM32_OTG1_NUMBER, STM32_USB_OTG1_IRQ_PRIORITY); - - /* - Forced device mode. - - USB turn-around time = TRDT_VALUE_FS. - - Full Speed 1.1 PHY.*/ - otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_FS) | - GUSBCFG_PHYSEL; - - /* 48MHz 1.1 PHY.*/ - otgp->DCFG = 0x02200000 | DCFG_DSPD_FS11; - } -#endif - -#if STM32_USB_USE_OTG2 - if (&USBD2 == usbp) { - /* OTG HS clock enable and reset.*/ - rccEnableOTG_HS(false); - rccResetOTG_HS(); - - /* ULPI clock is managed depending on the presence of an external - PHY.*/ -#if defined(BOARD_OTG2_USES_ULPI) - rccEnableOTG_HSULPI(true); -#else - /* Workaround for the problem described here: - http://forum.chibios.org/phpbb/viewtopic.php?f=16&t=1798.*/ - rccDisableOTG_HSULPI(true); -#endif - - /* Enables IRQ vector.*/ - nvicEnableVector(STM32_OTG2_NUMBER, STM32_USB_OTG2_IRQ_PRIORITY); - - /* - Forced device mode. - - USB turn-around time = TRDT_VALUE_HS or TRDT_VALUE_FS.*/ -#if defined(BOARD_OTG2_USES_ULPI) - /* High speed ULPI PHY.*/ - otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_HS) | - GUSBCFG_SRPCAP | GUSBCFG_HNPCAP; -#else - otgp->GUSBCFG = GUSBCFG_FDMOD | GUSBCFG_TRDT(TRDT_VALUE_FS) | - GUSBCFG_PHYSEL; -#endif - -#if defined(BOARD_OTG2_USES_ULPI) -#if STM32_USE_USB_OTG2_HS - /* USB 2.0 High Speed PHY in HS mode.*/ - otgp->DCFG = 0x02200000 | DCFG_DSPD_HS; -#else - /* USB 2.0 High Speed PHY in FS mode.*/ - otgp->DCFG = 0x02200000 | DCFG_DSPD_HS_FS; -#endif -#else - /* 48MHz 1.1 PHY.*/ - otgp->DCFG = 0x02200000 | DCFG_DSPD_FS11; -#endif - } -#endif - - /* Clearing mask of TXFIFOs to be filled.*/ - usbp->txpending = 0; - - /* PHY enabled.*/ - otgp->PCGCCTL = 0; - - /* VBUS sensing and transceiver enabled.*/ - otgp->GOTGCTL = GOTGCTL_BVALOEN | GOTGCTL_BVALOVAL; - -#if defined(BOARD_OTG2_USES_ULPI) -#if STM32_USB_USE_OTG1 - if (&USBD1 == usbp) { - otgp->GCCFG = GCCFG_INIT_VALUE; - } -#endif - -#if STM32_USB_USE_OTG2 - if (&USBD2 == usbp) { - otgp->GCCFG = 0; - } -#endif -#else - otgp->GCCFG = GCCFG_INIT_VALUE; -#endif - - /* Soft core reset.*/ - otg_core_reset(usbp); - - /* Interrupts on TXFIFOs half empty.*/ - otgp->GAHBCFG = 0; - - /* Endpoints re-initialization.*/ - otg_disable_ep(usbp); - - /* Clear all pending Device Interrupts, only the USB Reset interrupt - is required initially.*/ - otgp->DIEPMSK = 0; - otgp->DOEPMSK = 0; - otgp->DAINTMSK = 0; - if (usbp->config->sof_cb == NULL) - otgp->GINTMSK = GINTMSK_ENUMDNEM | GINTMSK_USBRSTM | GINTMSK_USBSUSPM | - GINTMSK_ESUSPM | GINTMSK_SRQM | GINTMSK_WKUM | - GINTMSK_IISOIXFRM | GINTMSK_IISOOXFRM; - else - otgp->GINTMSK = GINTMSK_ENUMDNEM | GINTMSK_USBRSTM | GINTMSK_USBSUSPM | - GINTMSK_ESUSPM | GINTMSK_SRQM | GINTMSK_WKUM | - GINTMSK_IISOIXFRM | GINTMSK_IISOOXFRM | - GINTMSK_SOFM; - - /* Clears all pending IRQs, if any. */ - otgp->GINTSTS = 0xFFFFFFFF; - -#if defined(_CHIBIOS_RT_) - /* Creates the data pump thread. Note, it is created only once.*/ - if (usbp->tr == NULL) { - thread_descriptor_t usbpump_descriptor = { - "usb_pump", - THD_WORKING_AREA_BASE(usbp->wa_pump), - THD_WORKING_AREA_END(usbp->wa_pump), - STM32_USB_OTG_THREAD_PRIO, - usb_lld_pump, - (void *)usbp - }; - - usbp->tr = chThdCreateI(&usbpump_descriptor); - chSchRescheduleS(); - } -#endif - - /* Global interrupts enable.*/ - otgp->GAHBCFG |= GAHBCFG_GINTMSK; - } -} - -/** - * @brief Deactivates the USB peripheral. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_stop(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - - /* If in ready state then disables the USB clock.*/ - if (usbp->state != USB_STOP) { - - /* Disabling all endpoints in case the driver has been stopped while - active.*/ - otg_disable_ep(usbp); - - usbp->txpending = 0; - - otgp->DAINTMSK = 0; - otgp->GAHBCFG = 0; - otgp->GCCFG = 0; - -#if STM32_USB_USE_OTG1 - if (&USBD1 == usbp) { - nvicDisableVector(STM32_OTG1_NUMBER); - rccDisableOTG_FS(false); - } -#endif - -#if STM32_USB_USE_OTG2 - if (&USBD2 == usbp) { - nvicDisableVector(STM32_OTG2_NUMBER); - rccDisableOTG_HS(false); -#if defined(BOARD_OTG2_USES_ULPI) - rccDisableOTG_HSULPI(true) -#endif - } -#endif - } -} - -/** - * @brief USB low level reset routine. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_reset(USBDriver *usbp) { - unsigned i; - stm32_otg_t *otgp = usbp->otg; - - /* Flush the Tx FIFO.*/ - otg_txfifo_flush(usbp, 0); - - /* Endpoint interrupts all disabled and cleared.*/ - otgp->DIEPEMPMSK = 0; - otgp->DAINTMSK = DAINTMSK_OEPM(0) | DAINTMSK_IEPM(0); - - /* All endpoints in NAK mode, interrupts cleared.*/ - for (i = 0; i <= usbp->otgparams->num_endpoints; i++) { - otgp->ie[i].DIEPCTL = DIEPCTL_SNAK; - otgp->oe[i].DOEPCTL = DOEPCTL_SNAK; - otgp->ie[i].DIEPINT = 0xFFFFFFFF; - otgp->oe[i].DOEPINT = 0xFFFFFFFF; - } - - /* Resets the FIFO memory allocator.*/ - otg_ram_reset(usbp); - - /* Receive FIFO size initialization, the address is always zero.*/ - otgp->GRXFSIZ = usbp->otgparams->rx_fifo_size; - otg_rxfifo_flush(usbp); - - /* Resets the device address to zero.*/ - otgp->DCFG = (otgp->DCFG & ~DCFG_DAD_MASK) | DCFG_DAD(0); - - /* Enables also EP-related interrupt sources.*/ - otgp->GINTMSK |= GINTMSK_RXFLVLM | GINTMSK_OEPM | GINTMSK_IEPM; - otgp->DIEPMSK = DIEPMSK_TOCM | DIEPMSK_XFRCM; - otgp->DOEPMSK = DOEPMSK_STUPM | DOEPMSK_XFRCM; - - /* EP0 initialization, it is a special case.*/ - usbp->epc[0] = &ep0config; - otgp->oe[0].DOEPTSIZ = 0; - otgp->oe[0].DOEPCTL = DOEPCTL_SD0PID | DOEPCTL_USBAEP | DOEPCTL_EPTYP_CTRL | - DOEPCTL_MPSIZ(ep0config.out_maxsize); - otgp->ie[0].DIEPTSIZ = 0; - otgp->ie[0].DIEPCTL = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_CTRL | - DIEPCTL_TXFNUM(0) | DIEPCTL_MPSIZ(ep0config.in_maxsize); - otgp->DIEPTXF0 = DIEPTXF_INEPTXFD(ep0config.in_maxsize / 4) | - DIEPTXF_INEPTXSA(otg_ram_alloc(usbp, - ep0config.in_maxsize / 4)); -} - -/** - * @brief Sets the USB address. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_set_address(USBDriver *usbp) { - stm32_otg_t *otgp = usbp->otg; - - otgp->DCFG = (otgp->DCFG & ~DCFG_DAD_MASK) | DCFG_DAD(usbp->address); -} - -/** - * @brief Enables an endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { - uint32_t ctl, fsize; - stm32_otg_t *otgp = usbp->otg; - - /* IN and OUT common parameters.*/ - switch (usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) { - case USB_EP_MODE_TYPE_CTRL: - ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_CTRL; - break; - case USB_EP_MODE_TYPE_ISOC: - ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_ISO; - break; - case USB_EP_MODE_TYPE_BULK: - ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_BULK; - break; - case USB_EP_MODE_TYPE_INTR: - ctl = DIEPCTL_SD0PID | DIEPCTL_USBAEP | DIEPCTL_EPTYP_INTR; - break; - default: - return; - } - - /* OUT endpoint activation or deactivation.*/ - otgp->oe[ep].DOEPTSIZ = 0; - if (usbp->epc[ep]->out_state != NULL) { - otgp->oe[ep].DOEPCTL = ctl | DOEPCTL_MPSIZ(usbp->epc[ep]->out_maxsize); - otgp->DAINTMSK |= DAINTMSK_OEPM(ep); - } - else { - otgp->oe[ep].DOEPCTL &= ~DOEPCTL_USBAEP; - otgp->DAINTMSK &= ~DAINTMSK_OEPM(ep); - } - - /* IN endpoint activation or deactivation.*/ - otgp->ie[ep].DIEPTSIZ = 0; - if (usbp->epc[ep]->in_state != NULL) { - /* FIFO allocation for the IN endpoint.*/ - fsize = usbp->epc[ep]->in_maxsize / 4; - if (usbp->epc[ep]->in_multiplier > 1) - fsize *= usbp->epc[ep]->in_multiplier; - otgp->DIEPTXF[ep - 1] = DIEPTXF_INEPTXFD(fsize) | - DIEPTXF_INEPTXSA(otg_ram_alloc(usbp, fsize)); - otg_txfifo_flush(usbp, ep); - - otgp->ie[ep].DIEPCTL = ctl | - DIEPCTL_TXFNUM(ep) | - DIEPCTL_MPSIZ(usbp->epc[ep]->in_maxsize); - otgp->DAINTMSK |= DAINTMSK_IEPM(ep); - } - else { - otgp->DIEPTXF[ep - 1] = 0x02000400; /* Reset value.*/ - otg_txfifo_flush(usbp, ep); - otgp->ie[ep].DIEPCTL &= ~DIEPCTL_USBAEP; - otgp->DAINTMSK &= ~DAINTMSK_IEPM(ep); - } -} - -/** - * @brief Disables all the active endpoints except the endpoint zero. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_disable_endpoints(USBDriver *usbp) { - - /* Resets the FIFO memory allocator.*/ - otg_ram_reset(usbp); - - /* Disabling all endpoints.*/ - otg_disable_ep(usbp); -} - -/** - * @brief Returns the status of an OUT endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @return The endpoint status. - * @retval EP_STATUS_DISABLED The endpoint is not active. - * @retval EP_STATUS_STALLED The endpoint is stalled. - * @retval EP_STATUS_ACTIVE The endpoint is active. - * - * @notapi - */ -usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { - uint32_t ctl; - - (void)usbp; - - ctl = usbp->otg->oe[ep].DOEPCTL; - if (!(ctl & DOEPCTL_USBAEP)) - return EP_STATUS_DISABLED; - if (ctl & DOEPCTL_STALL) - return EP_STATUS_STALLED; - return EP_STATUS_ACTIVE; -} - -/** - * @brief Returns the status of an IN endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @return The endpoint status. - * @retval EP_STATUS_DISABLED The endpoint is not active. - * @retval EP_STATUS_STALLED The endpoint is stalled. - * @retval EP_STATUS_ACTIVE The endpoint is active. - * - * @notapi - */ -usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { - uint32_t ctl; - - (void)usbp; - - ctl = usbp->otg->ie[ep].DIEPCTL; - if (!(ctl & DIEPCTL_USBAEP)) - return EP_STATUS_DISABLED; - if (ctl & DIEPCTL_STALL) - return EP_STATUS_STALLED; - return EP_STATUS_ACTIVE; -} - -/** - * @brief Reads a setup packet from the dedicated packet buffer. - * @details This function must be invoked in the context of the @p setup_cb - * callback in order to read the received setup packet. - * @pre In order to use this function the endpoint must have been - * initialized as a control endpoint. - * @post The endpoint is ready to accept another packet. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @param[out] buf buffer where to copy the packet data - * - * @notapi - */ -void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { - - memcpy(buf, usbp->epc[ep]->setup_buf, 8); -} - -/** - * @brief Starts a receive operation on an OUT endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { - uint32_t pcnt, rxsize; - USBOutEndpointState *osp = usbp->epc[ep]->out_state; - - /* Transfer initialization.*/ - osp->totsize = osp->rxsize; - if ((ep == 0) && (osp->rxsize > EP0_MAX_OUTSIZE)) - osp->rxsize = EP0_MAX_OUTSIZE; - - /* Transaction size is rounded to a multiple of packet size because the - following requirement in the RM: - "For OUT transfers, the transfer size field in the endpoint's transfer - size register must be a multiple of the maximum packet size of the - endpoint, adjusted to the Word boundary".*/ - pcnt = (osp->rxsize + usbp->epc[ep]->out_maxsize - 1U) / - usbp->epc[ep]->out_maxsize; - rxsize = (pcnt * usbp->epc[ep]->out_maxsize + 3U) & 0xFFFFFFFCU; - - /*Setting up transaction parameters in DOEPTSIZ.*/ - usbp->otg->oe[ep].DOEPTSIZ = DOEPTSIZ_STUPCNT(3) | DOEPTSIZ_PKTCNT(pcnt) | - DOEPTSIZ_XFRSIZ(rxsize); - - /* Special case of isochronous endpoint.*/ - if ((usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { - /* Odd/even bit toggling for isochronous endpoint.*/ - if (usbp->otg->DSTS & DSTS_FNSOF_ODD) - usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_SEVNFRM; - else - usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_SODDFRM; - } - - /* Starting operation.*/ - usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_EPENA | DOEPCTL_CNAK; -} - -/** - * @brief Starts a transmit operation on an IN endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { - USBInEndpointState *isp = usbp->epc[ep]->in_state; - - /* Transfer initialization.*/ - isp->totsize = isp->txsize; - if (isp->txsize == 0) { - /* Special case, sending zero size packet.*/ - usbp->otg->ie[ep].DIEPTSIZ = DIEPTSIZ_PKTCNT(1) | DIEPTSIZ_XFRSIZ(0); - } - else { - if ((ep == 0) && (isp->txsize > EP0_MAX_INSIZE)) - isp->txsize = EP0_MAX_INSIZE; - - /* Normal case.*/ - uint32_t pcnt = (isp->txsize + usbp->epc[ep]->in_maxsize - 1) / - usbp->epc[ep]->in_maxsize; - /* TODO: Support more than one packet per frame for isochronous transfers.*/ - usbp->otg->ie[ep].DIEPTSIZ = DIEPTSIZ_MCNT(1) | DIEPTSIZ_PKTCNT(pcnt) | - DIEPTSIZ_XFRSIZ(isp->txsize); - } - - /* Special case of isochronous endpoint.*/ - if ((usbp->epc[ep]->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) { - /* Odd/even bit toggling.*/ - if (usbp->otg->DSTS & DSTS_FNSOF_ODD) - usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_SEVNFRM; - else - usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_SODDFRM; - } - - /* Starting operation.*/ - usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_EPENA | DIEPCTL_CNAK; - usbp->otg->DIEPEMPMSK |= DIEPEMPMSK_INEPTXFEM(ep); -} - -/** - * @brief Brings an OUT endpoint in the stalled state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { - - usbp->otg->oe[ep].DOEPCTL |= DOEPCTL_STALL; -} - -/** - * @brief Brings an IN endpoint in the stalled state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { - - usbp->otg->ie[ep].DIEPCTL |= DIEPCTL_STALL; -} - -/** - * @brief Brings an OUT endpoint in the active state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { - - usbp->otg->oe[ep].DOEPCTL &= ~DOEPCTL_STALL; -} - -/** - * @brief Brings an IN endpoint in the active state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { - - usbp->otg->ie[ep].DIEPCTL &= ~DIEPCTL_STALL; -} - -/** - * @brief USB data transfer loop. - * @details This function must be executed by a system thread in order to - * make the USB driver work. - * @note The data copy part of the driver is implemented in this thread - * in order to not perform heavy tasks within interrupt handlers. - * - * @param[in] p pointer to the @p USBDriver object - * - * @special - */ -void usb_lld_pump(void *p) { - USBDriver *usbp = (USBDriver *)p; - stm32_otg_t *otgp = usbp->otg; - - osalSysLock(); - while (true) { - usbep_t ep; - uint32_t epmask; - - /* Nothing to do, going to sleep.*/ - if ((usbp->state == USB_STOP) || - ((usbp->txpending == 0) && !(otgp->GINTSTS & GINTSTS_RXFLVL))) { - otgp->GINTMSK |= GINTMSK_RXFLVLM; - osalThreadSuspendS(&usbp->wait); - } - osalSysUnlock(); - - /* Checks if there are TXFIFOs to be filled.*/ - for (ep = 0; ep <= usbp->otgparams->num_endpoints; ep++) { - - /* Empties the RX FIFO.*/ - while (otgp->GINTSTS & GINTSTS_RXFLVL) { - otg_rxfifo_handler(usbp); - } - - epmask = (1 << ep); - if (usbp->txpending & epmask) { - bool done; - - osalSysLock(); - /* USB interrupts are globally *suspended* because the peripheral - does not allow any interference during the TX FIFO filling - operation. - Synopsys document: DesignWare Cores USB 2.0 Hi-Speed On-The-Go (OTG) - "The application has to finish writing one complete packet before - switching to a different channel/endpoint FIFO. Violating this - rule results in an error.".*/ - otgp->GAHBCFG &= ~GAHBCFG_GINTMSK; - usbp->txpending &= ~epmask; - osalSysUnlock(); - - done = otg_txfifo_handler(usbp, ep); - - osalSysLock(); - otgp->GAHBCFG |= GAHBCFG_GINTMSK; - if (!done) - otgp->DIEPEMPMSK |= epmask; - osalSysUnlock(); - } - } - osalSysLock(); - } -} - -#endif /* HAL_USE_USB */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/OTGv1/usb_lld.h b/os/hal/ports/STM32/LLD/OTGv1/usb_lld.h deleted file mode 100644 index 8b3ee8957..000000000 --- a/os/hal/ports/STM32/LLD/OTGv1/usb_lld.h +++ /dev/null @@ -1,556 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/OTGv1/usb_lld.h - * @brief STM32 USB subsystem low level driver header. - * - * @addtogroup USB - * @{ - */ - -#ifndef _USB_LLD_H_ -#define _USB_LLD_H_ - -#if HAL_USE_USB || defined(__DOXYGEN__) - -#include "stm32_otg.h" - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Status stage handling method. - */ -#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW - -/** - * @brief The address can be changed immediately upon packet reception. - */ -#define USB_SET_ADDRESS_MODE USB_EARLY_SET_ADDRESS - -/** - * @brief Method for set address acknowledge. - */ -#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @brief OTG1 driver enable switch. - * @details If set to @p TRUE the support for OTG_FS is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_USB_USE_OTG1) || defined(__DOXYGEN__) -#define STM32_USB_USE_OTG1 FALSE -#endif - -/** - * @brief OTG2 driver enable switch. - * @details If set to @p TRUE the support for OTG_HS is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_USB_USE_OTG2) || defined(__DOXYGEN__) -#define STM32_USB_USE_OTG2 FALSE -#endif - -/** - * @brief OTG1 interrupt priority level setting. - */ -#if !defined(STM32_USB_OTG1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_USB_OTG1_IRQ_PRIORITY 14 -#endif - -/** - * @brief OTG2 interrupt priority level setting. - */ -#if !defined(STM32_USB_OTG2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_USB_OTG2_IRQ_PRIORITY 14 -#endif - -/** - * @brief OTG1 RX shared FIFO size. - * @note Must be a multiple of 4. - */ -#if !defined(STM32_USB_OTG1_RX_FIFO_SIZE) || defined(__DOXYGEN__) -#define STM32_USB_OTG1_RX_FIFO_SIZE 512 -#endif - -/** - * @brief OTG2 RX shared FIFO size. - * @note Must be a multiple of 4. - */ -#if !defined(STM32_USB_OTG2_RX_FIFO_SIZE) || defined(__DOXYGEN__) -#define STM32_USB_OTG2_RX_FIFO_SIZE 1024 -#endif - -/** - * @brief Enables HS mode on OTG2 else FS mode. - * @note The default is @p TRUE. - * @note Has effect only if @p BOARD_OTG2_USES_ULPI is defined. - */ -#if !defined(STM32_USE_USB_OTG2_HS) || defined(__DOXYGEN__) -#define STM32_USE_USB_OTG2_HS TRUE -#endif - -/** - * @brief Dedicated data pump threads priority. - */ -#if !defined(STM32_USB_OTG_THREAD_PRIO) || defined(__DOXYGEN__) -#define STM32_USB_OTG_THREAD_PRIO LOWPRIO -#endif - -/** - * @brief Dedicated data pump threads stack size. - */ -#if !defined(STM32_USB_OTG_THREAD_STACK_SIZE) || defined(__DOXYGEN__) -#define STM32_USB_OTG_THREAD_STACK_SIZE 128 -#endif - -/** - * @brief Exception priority level during TXFIFOs operations. - * @note Because an undocumented silicon behavior the operation of - * copying a packet into a TXFIFO must not be interrupted by - * any other operation on the OTG peripheral. - * This parameter represents the priority mask during copy - * operations. The default value only allows to call USB - * functions from callbacks invoked from USB ISR handlers. - * If you need to invoke USB functions from other handlers - * then raise this priority mast to the same level of the - * handler you need to use. - * @note The value zero means disabled, when disabled calling USB - * functions is only safe from thread level or from USB - * callbacks. - */ -#if !defined(STM32_USB_OTGFIFO_FILL_BASEPRI) || defined(__DOXYGEN__) -#define STM32_USB_OTGFIFO_FILL_BASEPRI 0 -#endif - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -/** - * @brief Maximum endpoint address. - */ -#if !STM32_USB_USE_OTG2 || defined(__DOXYGEN__) -#define USB_MAX_ENDPOINTS 3 -#else -#define USB_MAX_ENDPOINTS 5 -#endif - -#if STM32_USB_USE_OTG1 && !STM32_HAS_OTG1 -#error "OTG1 not present in the selected device" -#endif - -#if STM32_USB_USE_OTG2 && !STM32_HAS_OTG2 -#error "OTG2 not present in the selected device" -#endif - -#if !STM32_USB_USE_OTG1 && !STM32_USB_USE_OTG2 -#error "USB driver activated but no USB peripheral assigned" -#endif - -#if STM32_USB_USE_OTG1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_OTG1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to OTG1" -#endif - -#if STM32_USB_USE_OTG2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_OTG2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to OTG2" -#endif - -#if (STM32_USB_OTG1_RX_FIFO_SIZE & 3) != 0 -#error "OTG1 RX FIFO size must be a multiple of 4" -#endif - -#if (STM32_USB_OTG2_RX_FIFO_SIZE & 3) != 0 -#error "OTG2 RX FIFO size must be a multiple of 4" -#endif - -#if defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) -#define STM32_USBCLK STM32_PLL48CLK -#elif defined(STM32F10X_CL) -#define STM32_USBCLK STM32_OTGFSCLK -#elif defined(STM32L4XX) -#define STM32_USBCLK STM32_48CLK -#else -#error "unsupported STM32 platform for OTG functionality" -#endif - -#if STM32_USBCLK != 48000000 -#error "the USB OTG driver requires a 48MHz clock" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Peripheral-specific parameters block. - */ -typedef struct { - uint32_t rx_fifo_size; - uint32_t otg_ram_size; - uint32_t num_endpoints; -} stm32_otg_params_t; - -/** - * @brief Type of an IN endpoint state structure. - */ -typedef struct { - /** - * @brief Requested transmit transfer size. - */ - size_t txsize; - /** - * @brief Transmitted bytes so far. - */ - size_t txcnt; - /** - * @brief Pointer to the transmission linear buffer. - */ - const uint8_t *txbuf; -#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif - /* End of the mandatory fields.*/ - /** - * @brief Total transmit transfer size. - */ - size_t totsize; -} USBInEndpointState; - -/** - * @brief Type of an OUT endpoint state structure. - */ -typedef struct { - /** - * @brief Requested receive transfer size. - */ - size_t rxsize; - /** - * @brief Received bytes so far. - */ - size_t rxcnt; - /** - * @brief Pointer to the receive linear buffer. - */ - uint8_t *rxbuf; -#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif - /* End of the mandatory fields.*/ - /** - * @brief Total receive transfer size. - */ - size_t totsize; -} USBOutEndpointState; - -/** - * @brief Type of an USB endpoint configuration structure. - * @note Platform specific restrictions may apply to endpoints. - */ -typedef struct { - /** - * @brief Type and mode of the endpoint. - */ - uint32_t ep_mode; - /** - * @brief Setup packet notification callback. - * @details This callback is invoked when a setup packet has been - * received. - * @post The application must immediately call @p usbReadPacket() in - * order to access the received packet. - * @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL - * endpoints, it should be set to @p NULL for other endpoint - * types. - */ - usbepcallback_t setup_cb; - /** - * @brief IN endpoint notification callback. - * @details This field must be set to @p NULL if callback is not required. - */ - usbepcallback_t in_cb; - /** - * @brief OUT endpoint notification callback. - * @details This field must be set to @p NULL if callback is not required. - */ - usbepcallback_t out_cb; - /** - * @brief IN endpoint maximum packet size. - * @details This field must be set to zero if the IN endpoint is not used. - */ - uint16_t in_maxsize; - /** - * @brief OUT endpoint maximum packet size. - * @details This field must be set to zero if the OUT endpoint is not used. - */ - uint16_t out_maxsize; - /** - * @brief @p USBEndpointState associated to the IN endpoint. - * @details This field must be set to @p NULL if the IN endpoint is not - * used. - */ - USBInEndpointState *in_state; - /** - * @brief @p USBEndpointState associated to the OUT endpoint. - * @details This field must be set to @p NULL if the OUT endpoint is not - * used. - */ - USBOutEndpointState *out_state; - /* End of the mandatory fields.*/ - /** - * @brief Determines the space allocated for the TXFIFO as multiples of - * the packet size (@p in_maxsize). Note that zero is interpreted - * as one for simplicity and robustness. - */ - uint16_t in_multiplier; - /** - * @brief Pointer to a buffer for setup packets. - * @details Setup packets require a dedicated 8-bytes buffer, set this - * field to @p NULL for non-control endpoints. - */ - uint8_t *setup_buf; -} USBEndpointConfig; - -/** - * @brief Type of an USB driver configuration structure. - */ -typedef struct { - /** - * @brief USB events callback. - * @details This callback is invoked when an USB driver event is registered. - */ - usbeventcb_t event_cb; - /** - * @brief Device GET_DESCRIPTOR request callback. - * @note This callback is mandatory and cannot be set to @p NULL. - */ - usbgetdescriptor_t get_descriptor_cb; - /** - * @brief Requests hook callback. - * @details This hook allows to be notified of standard requests or to - * handle non standard requests. - */ - usbreqhandler_t requests_hook_cb; - /** - * @brief Start Of Frame callback. - */ - usbcallback_t sof_cb; - /* End of the mandatory fields.*/ -} USBConfig; - -/** - * @brief Structure representing an USB driver. - */ -struct USBDriver { - /** - * @brief Driver state. - */ - usbstate_t state; - /** - * @brief Current configuration data. - */ - const USBConfig *config; - /** - * @brief Bit map of the transmitting IN endpoints. - */ - uint16_t transmitting; - /** - * @brief Bit map of the receiving OUT endpoints. - */ - uint16_t receiving; - /** - * @brief Active endpoints configurations. - */ - const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1]; - /** - * @brief Fields available to user, it can be used to associate an - * application-defined handler to an IN endpoint. - * @note The base index is one, the endpoint zero does not have a - * reserved element in this array. - */ - void *in_params[USB_MAX_ENDPOINTS]; - /** - * @brief Fields available to user, it can be used to associate an - * application-defined handler to an OUT endpoint. - * @note The base index is one, the endpoint zero does not have a - * reserved element in this array. - */ - void *out_params[USB_MAX_ENDPOINTS]; - /** - * @brief Endpoint 0 state. - */ - usbep0state_t ep0state; - /** - * @brief Next position in the buffer to be transferred through endpoint 0. - */ - uint8_t *ep0next; - /** - * @brief Number of bytes yet to be transferred through endpoint 0. - */ - size_t ep0n; - /** - * @brief Endpoint 0 end transaction callback. - */ - usbcallback_t ep0endcb; - /** - * @brief Setup packet buffer. - */ - uint8_t setup[8]; - /** - * @brief Current USB device status. - */ - uint16_t status; - /** - * @brief Assigned USB address. - */ - uint8_t address; - /** - * @brief Current USB device configuration. - */ - uint8_t configuration; -#if defined(USB_DRIVER_EXT_FIELDS) - USB_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the OTG peripheral associated to this driver. - */ - stm32_otg_t *otg; - /** - * @brief Peripheral-specific parameters. - */ - const stm32_otg_params_t *otgparams; - /** - * @brief Pointer to the next address in the packet memory. - */ - uint32_t pmnext; - /** - * @brief Mask of TXFIFOs to be filled by the pump thread. - */ - uint32_t txpending; - /** - * @brief Pointer to the thread when it is sleeping or @p NULL. - */ - thread_reference_t wait; -#if defined(_CHIBIOS_RT_) - /** - * @brief Pointer to the thread. - */ - thread_reference_t tr; - /** - * @brief Working area for the dedicated data pump thread; - */ - THD_WORKING_AREA(wa_pump, STM32_USB_OTG_THREAD_STACK_SIZE); -#endif -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Returns the exact size of a receive transaction. - * @details The received size can be different from the size specified in - * @p usbStartReceiveI() because the last packet could have a size - * different from the expected one. - * @pre The OUT endpoint must have been configured in transaction mode - * in order to use this function. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @return Received data size. - * - * @notapi - */ -#define usb_lld_get_transaction_size(usbp, ep) \ - ((usbp)->epc[ep]->out_state->rxcnt) - -/** - * @brief Connects the USB device. - * - * @api - */ -#if defined(STM32F7XX) || defined(__DOXYGEN__) -#define usb_lld_connect_bus(usbp) ((usbp)->otg->DCTL &= ~DCTL_SDIS) -#else -#define usb_lld_connect_bus(usbp) ((usbp)->otg->GCCFG |= GCCFG_VBUSBSEN) -#endif - -/** - * @brief Disconnect the USB device. - * - * @api - */ -#if defined(STM32F7XX) || defined(__DOXYGEN__) -#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->DCTL |= DCTL_SDIS) -#else -#define usb_lld_disconnect_bus(usbp) ((usbp)->otg->GCCFG &= ~GCCFG_VBUSBSEN) -#endif - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_USB_USE_OTG1 && !defined(__DOXYGEN__) -extern USBDriver USBD1; -#endif - -#if STM32_USB_USE_OTG2 && !defined(__DOXYGEN__) -extern USBDriver USBD2; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void usb_lld_init(void); - void usb_lld_start(USBDriver *usbp); - void usb_lld_stop(USBDriver *usbp); - void usb_lld_reset(USBDriver *usbp); - void usb_lld_set_address(USBDriver *usbp); - void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep); - void usb_lld_disable_endpoints(USBDriver *usbp); - usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep); - usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep); - void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf); - void usb_lld_start_out(USBDriver *usbp, usbep_t ep); - void usb_lld_start_in(USBDriver *usbp, usbep_t ep); - void usb_lld_stall_out(USBDriver *usbp, usbep_t ep); - void usb_lld_stall_in(USBDriver *usbp, usbep_t ep); - void usb_lld_clear_out(USBDriver *usbp, usbep_t ep); - void usb_lld_clear_in(USBDriver *usbp, usbep_t ep); - void usb_lld_pump(void *p); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_USB */ - -#endif /* _USB_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.c b/os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.c new file mode 100644 index 000000000..de356031a --- /dev/null +++ b/os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.c @@ -0,0 +1,443 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/RTCv1/rtc_lld.c + * @brief STM32 RTC subsystem low level driver header. + * + * @addtogroup RTC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_RTC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief RTC driver identifier. + */ +RTCDriver RTCD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Wait for synchronization of RTC registers with APB1 bus. + * @details This function must be invoked before trying to read RTC registers + * in the backup domain: DIV, CNT, ALR. CR registers can always + * be read. + * + * @notapi + */ +static void rtc_apb1_sync(void) { + + while ((RTC->CRL & RTC_CRL_RSF) == 0) + ; +} + +/** + * @brief Wait for for previous write operation complete. + * @details This function must be invoked before writing to any RTC registers + * + * @notapi + */ +static void rtc_wait_write_completed(void) { + + while ((RTC->CRL & RTC_CRL_RTOFF) == 0) + ; +} + +/** + * @brief Acquires write access to RTC registers. + * @details Before writing to the backup domain RTC registers the previous + * write operation must be completed. Use this function before + * writing to PRL, CNT, ALR registers. + * + * @notapi + */ +static void rtc_acquire_access(void) { + + rtc_wait_write_completed(); + RTC->CRL |= RTC_CRL_CNF; +} + +/** + * @brief Releases write access to RTC registers. + * + * @notapi + */ +static void rtc_release_access(void) { + + RTC->CRL &= ~RTC_CRL_CNF; +} + +/** + * @brief Converts time from timespec to seconds counter. + * + * @param[in] timespec pointer to a @p RTCDateTime structure + * @return the TR register encoding. + * + * @notapi + */ +static time_t rtc_encode(const RTCDateTime *timespec) { + struct tm tim; + + rtcConvertDateTimeToStructTm(timespec, &tim, NULL); + return mktime(&tim); +} + +/** + * @brief Converts time from seconds/milliseconds to timespec. + * + * @param[in] tv_sec seconds value + * @param[in] tv_msec milliseconds value + * @param[out] timespec pointer to a @p RTCDateTime structure + * + * @notapi + */ +static void rtc_decode(uint32_t tv_sec, + uint32_t tv_msec, + RTCDateTime *timespec) { + struct tm tim; + struct tm *t; + + /* If the conversion is successful the function returns a pointer + to the object the result was written into.*/ +#if defined(__GNUC__) || defined(__CC_ARM) + t = localtime_r((time_t *)&(tv_sec), &tim); + osalDbgAssert(t != NULL, "conversion failed"); +#else + struct tm *t = localtime(&tv_sec); + memcpy(&tim, t, sizeof(struct tm)); +#endif + + rtcConvertStructTmToDateTime(&tim, tv_msec, timespec); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/** + * @brief RTC interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_RTC1_HANDLER) { + uint16_t flags; + + OSAL_IRQ_PROLOGUE(); + + /* Code hits this wait only when AHB1 bus was previously powered off by any + reason (standby, reset, etc). In other cases there is no waiting.*/ + rtc_apb1_sync(); + + /* Mask of all enabled and pending sources.*/ + flags = RTCD1.rtc->CRH & RTCD1.rtc->CRL; + RTCD1.rtc->CRL &= ~(RTC_CRL_SECF | RTC_CRL_ALRF | RTC_CRL_OWF); + + if (flags & RTC_CRL_SECF) + RTCD1.callback(&RTCD1, RTC_EVENT_SECOND); + + if (flags & RTC_CRL_ALRF) + RTCD1.callback(&RTCD1, RTC_EVENT_ALARM); + + if (flags & RTC_CRL_OWF) + RTCD1.callback(&RTCD1, RTC_EVENT_OVERFLOW); + + OSAL_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Load value of RTCCLK to prescaler registers. + * @note The pre-scaler must not be set on every reset as RTC clock + * counts are lost when it is set. + * @note This function designed to be called from + * hal_lld_backup_domain_init(). Because there is only place + * where possible to detect BKP domain reset event reliably. + * + * @notapi + */ +void rtc_lld_set_prescaler(void) { + syssts_t sts; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + rtc_acquire_access(); + RTC->PRLH = (uint16_t)((STM32_RTCCLK - 1) >> 16) & 0x000F; + RTC->PRLL = (uint16_t)(((STM32_RTCCLK - 1)) & 0xFFFF); + rtc_release_access(); + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Initialize RTC. + * + * @notapi + */ +void rtc_lld_init(void) { + + /* RTC object initialization.*/ + rtcObjectInit(&RTCD1); + + /* RTC pointer initialization.*/ + RTCD1.rtc = RTC; + + /* RSF bit must be cleared by software after an APB1 reset or an APB1 clock + stop. Otherwise its value will not be actual. */ + RTCD1.rtc->CRL &= ~RTC_CRL_RSF; + + /* Required because access to PRL.*/ + rtc_apb1_sync(); + + /* All interrupts initially disabled.*/ + rtc_wait_write_completed(); + RTCD1.rtc->CRH = 0; + + /* Callback initially disabled.*/ + RTCD1.callback = NULL; + + /* IRQ vector permanently assigned to this driver.*/ + nvicEnableVector(STM32_RTC1_NUMBER, STM32_RTC_IRQ_PRIORITY); +} + +/** + * @brief Set current time. + * @note Fractional part will be silently ignored. There is no possibility + * to change it on STM32F1xx platform. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] timespec pointer to a @p RTCTime structure + * + * @notapi + */ +void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec) { + time_t tv_sec = rtc_encode(timespec); + + rtcSTM32SetSec(rtcp, tv_sec); +} + +/** + * @brief Get current time. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] timespec pointer to a @p RTCTime structure + * + * @notapi + */ +void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec) { + uint32_t tv_sec, tv_msec; + + rtcSTM32GetSecMsec(rtcp, &tv_sec, &tv_msec); + rtc_decode(tv_sec, tv_msec, timespec); +} + +/** + * @brief Set alarm time. + * + * @note Default value after BKP domain reset is 0xFFFFFFFF + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] alarm alarm identifier + * @param[in] alarmspec pointer to a @p RTCAlarm structure + * + * @notapi + */ +void rtc_lld_set_alarm(RTCDriver *rtcp, + rtcalarm_t alarm_number, + const RTCAlarm *alarmspec) { + syssts_t sts; + (void)alarm_number; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + rtc_acquire_access(); + if (alarmspec != NULL) { + rtcp->rtc->ALRH = (uint16_t)(alarmspec->tv_sec >> 16); + rtcp->rtc->ALRL = (uint16_t)(alarmspec->tv_sec & 0xFFFF); + } + else { + rtcp->rtc->ALRH = 0; + rtcp->rtc->ALRL = 0; + } + rtc_release_access(); + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Get current alarm. + * @note If an alarm has not been set then the returned alarm specification + * is not meaningful. + * @note The function can be called from any context. + * @note Default value after BKP domain reset is 0xFFFFFFFF. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] alarm alarm identifier + * @param[out] alarmspec pointer to a @p RTCAlarm structure + * + * @notapi + */ +void rtc_lld_get_alarm(RTCDriver *rtcp, + rtcalarm_t alarm_number, + RTCAlarm *alarmspec) { + syssts_t sts; + (void)alarm_number; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + /* Required because access to ALR.*/ + rtc_apb1_sync(); + + alarmspec->tv_sec = ((rtcp->rtc->ALRH << 16) + rtcp->rtc->ALRL); + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Enables or disables RTC callbacks. + * @details This function enables or disables callbacks, use a @p NULL pointer + * in order to disable a callback. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] callback callback function pointer or @p NULL + * + * @notapi + */ +void rtc_lld_set_callback(RTCDriver *rtcp, rtccb_t callback) { + syssts_t sts; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + if (callback != NULL) { + + /* IRQ sources enabled only after setting up the callback.*/ + rtcp->callback = callback; + + rtc_wait_write_completed(); + rtcp->rtc->CRL &= ~(RTC_CRL_OWF | RTC_CRL_ALRF | RTC_CRL_SECF); + rtcp->rtc->CRH = RTC_CRH_OWIE | RTC_CRH_ALRIE | RTC_CRH_SECIE; + } + else { + rtc_wait_write_completed(); + rtcp->rtc->CRH = 0; + + /* Callback set to NULL only after disabling the IRQ sources.*/ + rtcp->callback = NULL; + } + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Get seconds and (optionally) milliseconds from RTC. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[out] tv_sec pointer to seconds value + * @param[out] tv_msec pointer to milliseconds value, set it + * to @p NULL if not needed + * + * @api + */ +void rtcSTM32GetSecMsec(RTCDriver *rtcp, uint32_t *tv_sec, uint32_t *tv_msec) { + uint32_t time_frac; + syssts_t sts; + + osalDbgCheck((NULL != tv_sec) && (NULL != rtcp)); + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + /* Required because access to CNT and DIV.*/ + rtc_apb1_sync(); + + /* Loops until two consecutive read returning the same value.*/ + do { + *tv_sec = ((uint32_t)(rtcp->rtc->CNTH) << 16) + rtcp->rtc->CNTL; + time_frac = (((uint32_t)rtcp->rtc->DIVH) << 16) + (uint32_t)rtcp->rtc->DIVL; + } while ((*tv_sec) != (((uint32_t)(rtcp->rtc->CNTH) << 16) + rtcp->rtc->CNTL)); + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); + + if (NULL != tv_msec) + *tv_msec = (((uint32_t)STM32_RTCCLK - 1 - time_frac) * 1000) / STM32_RTCCLK; +} + +/** + * @brief Set seconds in RTC. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] tv_sec seconds value + * + * @api + */ +void rtcSTM32SetSec(RTCDriver *rtcp, uint32_t tv_sec) { + syssts_t sts; + + osalDbgCheck(NULL != rtcp); + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + rtc_acquire_access(); + rtcp->rtc->CNTH = (uint16_t)(tv_sec >> 16); + rtcp->rtc->CNTL = (uint16_t)(tv_sec & 0xFFFF); + rtc_release_access(); + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +#endif /* HAL_USE_RTC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.h b/os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.h new file mode 100644 index 000000000..04df16e11 --- /dev/null +++ b/os/hal/ports/STM32/LLD/RTCv1/hal_rtc_lld.h @@ -0,0 +1,198 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/RTCv1/rtc_lld.h + * @brief STM32F1xx RTC subsystem low level driver header. + * + * @addtogroup RTC + * @{ + */ + +#ifndef _RTC_LLD_H_ +#define _RTC_LLD_H_ + +#if HAL_USE_RTC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Implementation capabilities + */ +/** + * @brief This RTC implementation supports callbacks. + */ +#define RTC_SUPPORTS_CALLBACKS TRUE + +/** + * @brief One alarm comparator available. + */ +#define RTC_ALARMS 1 + +/** + * @brief Presence of a local persistent storage. + */ +#define RTC_HAS_STORAGE FALSE +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/* + * RTC driver system settings. + */ +#define STM32_RTC_IRQ_PRIORITY 15 +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if HAL_USE_RTC && !STM32_HAS_RTC +#error "RTC not present in the selected device" +#endif + +#if STM32_RTCCLK == 0 +#error "RTC clock not enabled" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief FileStream specific methods. + */ +#define _rtc_driver_methods \ + _file_stream_methods + +/** + * @brief Type of a structure representing an RTC alarm time stamp. + */ +typedef struct RTCAlarm RTCAlarm; + +/** + * @brief Type of an RTC alarm. + * @details Meaningful on platforms with more than 1 alarm comparator. + */ +typedef uint32_t rtcalarm_t; + +/** + * @brief Type of an RTC event. + */ +typedef enum { + RTC_EVENT_SECOND = 0, /** Triggered every second. */ + RTC_EVENT_ALARM = 1, /** Triggered on alarm. */ + RTC_EVENT_OVERFLOW = 2 /** Triggered on counter overflow. */ +} rtcevent_t; + +/** + * @brief Type of a generic RTC callback. + */ +typedef void (*rtccb_t)(RTCDriver *rtcp, rtcevent_t event); + +/** + * @brief Structure representing an RTC alarm time stamp. + */ +struct RTCAlarm { + /** + * @brief Seconds since UNIX epoch. + */ + uint32_t tv_sec; +}; + +#if RTC_HAS_STORAGE || defined(__DOXYGEN__) +/** + * @extends FileStream + * + * @brief @p RTCDriver virtual methods table. + */ +struct RTCDriverVMT { + _rtc_driver_methods +}; +#endif + +/** + * @brief Structure representing an RTC driver. + */ +struct RTCDriver{ +#if RTC_HAS_STORAGE || defined(__DOXYGEN__) + /** + * @brief Virtual Methods Table. + */ + const struct RTCDriverVMT *vmt; +#endif + /** + * @brief Pointer to the RTC registers block. + */ + RTC_TypeDef *rtc; + /** + * @brief Callback pointer. + */ + rtccb_t callback; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern RTCDriver RTCD1; +#if RTC_HAS_STORAGE +extern struct RTCDriverVMT _rtc_lld_vmt; +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void rtc_lld_set_prescaler(void); + void rtc_lld_init(void); + void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec); + void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec); + void rtc_lld_set_alarm(RTCDriver *rtcp, + rtcalarm_t alarm_number, + const RTCAlarm *alarmspec); + void rtc_lld_get_alarm(RTCDriver *rtcp, + rtcalarm_t alarm_number, + RTCAlarm *alarmspec); + void rtc_lld_set_callback(RTCDriver *rtcp, rtccb_t callback); + void rtcSTM32GetSecMsec(RTCDriver *rtcp, uint32_t *tv_sec, uint32_t *tv_msec); + void rtcSTM32SetSec(RTCDriver *rtcp, uint32_t tv_sec); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_RTC */ + +#endif /* _RTC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv1/rtc_lld.c b/os/hal/ports/STM32/LLD/RTCv1/rtc_lld.c deleted file mode 100644 index de356031a..000000000 --- a/os/hal/ports/STM32/LLD/RTCv1/rtc_lld.c +++ /dev/null @@ -1,443 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/RTCv1/rtc_lld.c - * @brief STM32 RTC subsystem low level driver header. - * - * @addtogroup RTC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_RTC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief RTC driver identifier. - */ -RTCDriver RTCD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Wait for synchronization of RTC registers with APB1 bus. - * @details This function must be invoked before trying to read RTC registers - * in the backup domain: DIV, CNT, ALR. CR registers can always - * be read. - * - * @notapi - */ -static void rtc_apb1_sync(void) { - - while ((RTC->CRL & RTC_CRL_RSF) == 0) - ; -} - -/** - * @brief Wait for for previous write operation complete. - * @details This function must be invoked before writing to any RTC registers - * - * @notapi - */ -static void rtc_wait_write_completed(void) { - - while ((RTC->CRL & RTC_CRL_RTOFF) == 0) - ; -} - -/** - * @brief Acquires write access to RTC registers. - * @details Before writing to the backup domain RTC registers the previous - * write operation must be completed. Use this function before - * writing to PRL, CNT, ALR registers. - * - * @notapi - */ -static void rtc_acquire_access(void) { - - rtc_wait_write_completed(); - RTC->CRL |= RTC_CRL_CNF; -} - -/** - * @brief Releases write access to RTC registers. - * - * @notapi - */ -static void rtc_release_access(void) { - - RTC->CRL &= ~RTC_CRL_CNF; -} - -/** - * @brief Converts time from timespec to seconds counter. - * - * @param[in] timespec pointer to a @p RTCDateTime structure - * @return the TR register encoding. - * - * @notapi - */ -static time_t rtc_encode(const RTCDateTime *timespec) { - struct tm tim; - - rtcConvertDateTimeToStructTm(timespec, &tim, NULL); - return mktime(&tim); -} - -/** - * @brief Converts time from seconds/milliseconds to timespec. - * - * @param[in] tv_sec seconds value - * @param[in] tv_msec milliseconds value - * @param[out] timespec pointer to a @p RTCDateTime structure - * - * @notapi - */ -static void rtc_decode(uint32_t tv_sec, - uint32_t tv_msec, - RTCDateTime *timespec) { - struct tm tim; - struct tm *t; - - /* If the conversion is successful the function returns a pointer - to the object the result was written into.*/ -#if defined(__GNUC__) || defined(__CC_ARM) - t = localtime_r((time_t *)&(tv_sec), &tim); - osalDbgAssert(t != NULL, "conversion failed"); -#else - struct tm *t = localtime(&tv_sec); - memcpy(&tim, t, sizeof(struct tm)); -#endif - - rtcConvertStructTmToDateTime(&tim, tv_msec, timespec); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/** - * @brief RTC interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_RTC1_HANDLER) { - uint16_t flags; - - OSAL_IRQ_PROLOGUE(); - - /* Code hits this wait only when AHB1 bus was previously powered off by any - reason (standby, reset, etc). In other cases there is no waiting.*/ - rtc_apb1_sync(); - - /* Mask of all enabled and pending sources.*/ - flags = RTCD1.rtc->CRH & RTCD1.rtc->CRL; - RTCD1.rtc->CRL &= ~(RTC_CRL_SECF | RTC_CRL_ALRF | RTC_CRL_OWF); - - if (flags & RTC_CRL_SECF) - RTCD1.callback(&RTCD1, RTC_EVENT_SECOND); - - if (flags & RTC_CRL_ALRF) - RTCD1.callback(&RTCD1, RTC_EVENT_ALARM); - - if (flags & RTC_CRL_OWF) - RTCD1.callback(&RTCD1, RTC_EVENT_OVERFLOW); - - OSAL_IRQ_EPILOGUE(); -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Load value of RTCCLK to prescaler registers. - * @note The pre-scaler must not be set on every reset as RTC clock - * counts are lost when it is set. - * @note This function designed to be called from - * hal_lld_backup_domain_init(). Because there is only place - * where possible to detect BKP domain reset event reliably. - * - * @notapi - */ -void rtc_lld_set_prescaler(void) { - syssts_t sts; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - rtc_acquire_access(); - RTC->PRLH = (uint16_t)((STM32_RTCCLK - 1) >> 16) & 0x000F; - RTC->PRLL = (uint16_t)(((STM32_RTCCLK - 1)) & 0xFFFF); - rtc_release_access(); - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Initialize RTC. - * - * @notapi - */ -void rtc_lld_init(void) { - - /* RTC object initialization.*/ - rtcObjectInit(&RTCD1); - - /* RTC pointer initialization.*/ - RTCD1.rtc = RTC; - - /* RSF bit must be cleared by software after an APB1 reset or an APB1 clock - stop. Otherwise its value will not be actual. */ - RTCD1.rtc->CRL &= ~RTC_CRL_RSF; - - /* Required because access to PRL.*/ - rtc_apb1_sync(); - - /* All interrupts initially disabled.*/ - rtc_wait_write_completed(); - RTCD1.rtc->CRH = 0; - - /* Callback initially disabled.*/ - RTCD1.callback = NULL; - - /* IRQ vector permanently assigned to this driver.*/ - nvicEnableVector(STM32_RTC1_NUMBER, STM32_RTC_IRQ_PRIORITY); -} - -/** - * @brief Set current time. - * @note Fractional part will be silently ignored. There is no possibility - * to change it on STM32F1xx platform. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] timespec pointer to a @p RTCTime structure - * - * @notapi - */ -void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec) { - time_t tv_sec = rtc_encode(timespec); - - rtcSTM32SetSec(rtcp, tv_sec); -} - -/** - * @brief Get current time. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] timespec pointer to a @p RTCTime structure - * - * @notapi - */ -void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec) { - uint32_t tv_sec, tv_msec; - - rtcSTM32GetSecMsec(rtcp, &tv_sec, &tv_msec); - rtc_decode(tv_sec, tv_msec, timespec); -} - -/** - * @brief Set alarm time. - * - * @note Default value after BKP domain reset is 0xFFFFFFFF - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] alarm alarm identifier - * @param[in] alarmspec pointer to a @p RTCAlarm structure - * - * @notapi - */ -void rtc_lld_set_alarm(RTCDriver *rtcp, - rtcalarm_t alarm_number, - const RTCAlarm *alarmspec) { - syssts_t sts; - (void)alarm_number; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - rtc_acquire_access(); - if (alarmspec != NULL) { - rtcp->rtc->ALRH = (uint16_t)(alarmspec->tv_sec >> 16); - rtcp->rtc->ALRL = (uint16_t)(alarmspec->tv_sec & 0xFFFF); - } - else { - rtcp->rtc->ALRH = 0; - rtcp->rtc->ALRL = 0; - } - rtc_release_access(); - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Get current alarm. - * @note If an alarm has not been set then the returned alarm specification - * is not meaningful. - * @note The function can be called from any context. - * @note Default value after BKP domain reset is 0xFFFFFFFF. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] alarm alarm identifier - * @param[out] alarmspec pointer to a @p RTCAlarm structure - * - * @notapi - */ -void rtc_lld_get_alarm(RTCDriver *rtcp, - rtcalarm_t alarm_number, - RTCAlarm *alarmspec) { - syssts_t sts; - (void)alarm_number; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - /* Required because access to ALR.*/ - rtc_apb1_sync(); - - alarmspec->tv_sec = ((rtcp->rtc->ALRH << 16) + rtcp->rtc->ALRL); - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Enables or disables RTC callbacks. - * @details This function enables or disables callbacks, use a @p NULL pointer - * in order to disable a callback. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] callback callback function pointer or @p NULL - * - * @notapi - */ -void rtc_lld_set_callback(RTCDriver *rtcp, rtccb_t callback) { - syssts_t sts; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - if (callback != NULL) { - - /* IRQ sources enabled only after setting up the callback.*/ - rtcp->callback = callback; - - rtc_wait_write_completed(); - rtcp->rtc->CRL &= ~(RTC_CRL_OWF | RTC_CRL_ALRF | RTC_CRL_SECF); - rtcp->rtc->CRH = RTC_CRH_OWIE | RTC_CRH_ALRIE | RTC_CRH_SECIE; - } - else { - rtc_wait_write_completed(); - rtcp->rtc->CRH = 0; - - /* Callback set to NULL only after disabling the IRQ sources.*/ - rtcp->callback = NULL; - } - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Get seconds and (optionally) milliseconds from RTC. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[out] tv_sec pointer to seconds value - * @param[out] tv_msec pointer to milliseconds value, set it - * to @p NULL if not needed - * - * @api - */ -void rtcSTM32GetSecMsec(RTCDriver *rtcp, uint32_t *tv_sec, uint32_t *tv_msec) { - uint32_t time_frac; - syssts_t sts; - - osalDbgCheck((NULL != tv_sec) && (NULL != rtcp)); - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - /* Required because access to CNT and DIV.*/ - rtc_apb1_sync(); - - /* Loops until two consecutive read returning the same value.*/ - do { - *tv_sec = ((uint32_t)(rtcp->rtc->CNTH) << 16) + rtcp->rtc->CNTL; - time_frac = (((uint32_t)rtcp->rtc->DIVH) << 16) + (uint32_t)rtcp->rtc->DIVL; - } while ((*tv_sec) != (((uint32_t)(rtcp->rtc->CNTH) << 16) + rtcp->rtc->CNTL)); - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); - - if (NULL != tv_msec) - *tv_msec = (((uint32_t)STM32_RTCCLK - 1 - time_frac) * 1000) / STM32_RTCCLK; -} - -/** - * @brief Set seconds in RTC. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] tv_sec seconds value - * - * @api - */ -void rtcSTM32SetSec(RTCDriver *rtcp, uint32_t tv_sec) { - syssts_t sts; - - osalDbgCheck(NULL != rtcp); - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - rtc_acquire_access(); - rtcp->rtc->CNTH = (uint16_t)(tv_sec >> 16); - rtcp->rtc->CNTL = (uint16_t)(tv_sec & 0xFFFF); - rtc_release_access(); - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -#endif /* HAL_USE_RTC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv1/rtc_lld.h b/os/hal/ports/STM32/LLD/RTCv1/rtc_lld.h deleted file mode 100644 index 04df16e11..000000000 --- a/os/hal/ports/STM32/LLD/RTCv1/rtc_lld.h +++ /dev/null @@ -1,198 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/RTCv1/rtc_lld.h - * @brief STM32F1xx RTC subsystem low level driver header. - * - * @addtogroup RTC - * @{ - */ - -#ifndef _RTC_LLD_H_ -#define _RTC_LLD_H_ - -#if HAL_USE_RTC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Implementation capabilities - */ -/** - * @brief This RTC implementation supports callbacks. - */ -#define RTC_SUPPORTS_CALLBACKS TRUE - -/** - * @brief One alarm comparator available. - */ -#define RTC_ALARMS 1 - -/** - * @brief Presence of a local persistent storage. - */ -#define RTC_HAS_STORAGE FALSE -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/* - * RTC driver system settings. - */ -#define STM32_RTC_IRQ_PRIORITY 15 -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if HAL_USE_RTC && !STM32_HAS_RTC -#error "RTC not present in the selected device" -#endif - -#if STM32_RTCCLK == 0 -#error "RTC clock not enabled" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief FileStream specific methods. - */ -#define _rtc_driver_methods \ - _file_stream_methods - -/** - * @brief Type of a structure representing an RTC alarm time stamp. - */ -typedef struct RTCAlarm RTCAlarm; - -/** - * @brief Type of an RTC alarm. - * @details Meaningful on platforms with more than 1 alarm comparator. - */ -typedef uint32_t rtcalarm_t; - -/** - * @brief Type of an RTC event. - */ -typedef enum { - RTC_EVENT_SECOND = 0, /** Triggered every second. */ - RTC_EVENT_ALARM = 1, /** Triggered on alarm. */ - RTC_EVENT_OVERFLOW = 2 /** Triggered on counter overflow. */ -} rtcevent_t; - -/** - * @brief Type of a generic RTC callback. - */ -typedef void (*rtccb_t)(RTCDriver *rtcp, rtcevent_t event); - -/** - * @brief Structure representing an RTC alarm time stamp. - */ -struct RTCAlarm { - /** - * @brief Seconds since UNIX epoch. - */ - uint32_t tv_sec; -}; - -#if RTC_HAS_STORAGE || defined(__DOXYGEN__) -/** - * @extends FileStream - * - * @brief @p RTCDriver virtual methods table. - */ -struct RTCDriverVMT { - _rtc_driver_methods -}; -#endif - -/** - * @brief Structure representing an RTC driver. - */ -struct RTCDriver{ -#if RTC_HAS_STORAGE || defined(__DOXYGEN__) - /** - * @brief Virtual Methods Table. - */ - const struct RTCDriverVMT *vmt; -#endif - /** - * @brief Pointer to the RTC registers block. - */ - RTC_TypeDef *rtc; - /** - * @brief Callback pointer. - */ - rtccb_t callback; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern RTCDriver RTCD1; -#if RTC_HAS_STORAGE -extern struct RTCDriverVMT _rtc_lld_vmt; -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void rtc_lld_set_prescaler(void); - void rtc_lld_init(void); - void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec); - void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec); - void rtc_lld_set_alarm(RTCDriver *rtcp, - rtcalarm_t alarm_number, - const RTCAlarm *alarmspec); - void rtc_lld_get_alarm(RTCDriver *rtcp, - rtcalarm_t alarm_number, - RTCAlarm *alarmspec); - void rtc_lld_set_callback(RTCDriver *rtcp, rtccb_t callback); - void rtcSTM32GetSecMsec(RTCDriver *rtcp, uint32_t *tv_sec, uint32_t *tv_msec); - void rtcSTM32SetSec(RTCDriver *rtcp, uint32_t tv_sec); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_RTC */ - -#endif /* _RTC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.c b/os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.c new file mode 100644 index 000000000..ed78e121e --- /dev/null +++ b/os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.c @@ -0,0 +1,548 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/RTCv2/rtc_lld.c + * @brief STM32L1xx/STM32F2xx/STM32F4xx RTC low level driver. + * + * @addtogroup RTC + * @{ + */ + +#include "hal.h" + +#if HAL_USE_RTC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define RTC_TR_PM_OFFSET 22 +#define RTC_TR_HT_OFFSET 20 +#define RTC_TR_HU_OFFSET 16 +#define RTC_TR_MNT_OFFSET 12 +#define RTC_TR_MNU_OFFSET 8 +#define RTC_TR_ST_OFFSET 4 +#define RTC_TR_SU_OFFSET 0 + +#define RTC_DR_YT_OFFSET 20 +#define RTC_DR_YU_OFFSET 16 +#define RTC_DR_WDU_OFFSET 13 +#define RTC_DR_MT_OFFSET 12 +#define RTC_DR_MU_OFFSET 8 +#define RTC_DR_DT_OFFSET 4 +#define RTC_DR_DU_OFFSET 0 + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief RTC driver identifier. + */ +RTCDriver RTCD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Beginning of configuration procedure. + * + * @notapi + */ +static void rtc_enter_init(void) { + + RTCD1.rtc->ISR |= RTC_ISR_INIT; + while ((RTCD1.rtc->ISR & RTC_ISR_INITF) == 0) + ; +} + +/** + * @brief Finalizing of configuration procedure. + * + * @notapi + */ +static inline void rtc_exit_init(void) { + + RTCD1.rtc->ISR &= ~RTC_ISR_INIT; +} + +/** + * @brief Converts time from TR register encoding to timespec. + * + * @param[in] tr TR register value + * @param[out] timespec pointer to a @p RTCDateTime structure + * + * @notapi + */ +static void rtc_decode_time(uint32_t tr, RTCDateTime *timespec) { + uint32_t n; + + n = ((tr >> RTC_TR_HT_OFFSET) & 3) * 36000000; + n += ((tr >> RTC_TR_HU_OFFSET) & 15) * 3600000; + n += ((tr >> RTC_TR_MNT_OFFSET) & 7) * 600000; + n += ((tr >> RTC_TR_MNU_OFFSET) & 15) * 60000; + n += ((tr >> RTC_TR_ST_OFFSET) & 7) * 10000; + n += ((tr >> RTC_TR_SU_OFFSET) & 15) * 1000; + timespec->millisecond = n; +} + +/** + * @brief Converts date from DR register encoding to timespec. + * + * @param[in] dr DR register value + * @param[out] timespec pointer to a @p RTCDateTime structure + * + * @notapi + */ +static void rtc_decode_date(uint32_t dr, RTCDateTime *timespec) { + + timespec->year = (((dr >> RTC_DR_YT_OFFSET) & 15) * 10) + + ((dr >> RTC_DR_YU_OFFSET) & 15); + timespec->month = (((dr >> RTC_TR_MNT_OFFSET) & 1) * 10) + + ((dr >> RTC_TR_MNU_OFFSET) & 15); + timespec->day = (((dr >> RTC_DR_DT_OFFSET) & 3) * 10) + + ((dr >> RTC_DR_DU_OFFSET) & 15); + timespec->dayofweek = (dr >> RTC_DR_WDU_OFFSET) & 7; +} + +/** + * @brief Converts time from timespec to TR register encoding. + * + * @param[in] timespec pointer to a @p RTCDateTime structure + * @return the TR register encoding. + * + * @notapi + */ +static uint32_t rtc_encode_time(const RTCDateTime *timespec) { + uint32_t n, tr = 0; + + /* Subseconds cannot be set.*/ + n = timespec->millisecond / 1000; + + /* Seconds conversion.*/ + tr = tr | ((n % 10) << RTC_TR_SU_OFFSET); + n /= 10; + tr = tr | ((n % 6) << RTC_TR_ST_OFFSET); + n /= 6; + + /* Minutes conversion.*/ + tr = tr | ((n % 10) << RTC_TR_MNU_OFFSET); + n /= 10; + tr = tr | ((n % 6) << RTC_TR_MNT_OFFSET); + n /= 6; + + /* Hours conversion.*/ + tr = tr | ((n % 10) << RTC_TR_HU_OFFSET); + n /= 10; + tr = tr | (n << RTC_TR_HT_OFFSET); + + return tr; +} + +/** + * @brief Converts a date from timespec to DR register encoding. + * + * @param[in] timespec pointer to a @p RTCDateTime structure + * @return the DR register encoding. + * + * @notapi + */ +static uint32_t rtc_encode_date(const RTCDateTime *timespec) { + uint32_t n, dr = 0; + + /* Year conversion. Note, only years last two digits are considered.*/ + n = timespec->year; + dr = dr | ((n % 10) << RTC_DR_YU_OFFSET); + n /= 10; + dr = dr | ((n % 10) << RTC_DR_YT_OFFSET); + + /* Months conversion.*/ + n = timespec->month; + dr = dr | ((n % 10) << RTC_DR_MU_OFFSET); + n /= 10; + dr = dr | ((n % 10) << RTC_DR_MT_OFFSET); + + /* Days conversion.*/ + n = timespec->day; + dr = dr | ((n % 10) << RTC_DR_DU_OFFSET); + n /= 10; + dr = dr | ((n % 10) << RTC_DR_DT_OFFSET); + + /* Days of week conversion.*/ + dr = dr | (timespec->dayofweek << RTC_DR_WDU_OFFSET); + + return dr; +} + +#if RTC_HAS_STORAGE +/* TODO: Map on the backup SRAM on devices that have it.*/ +static size_t _write(void *instance, const uint8_t *bp, size_t n) { + + (void)instance; + (void)bp; + (void)n; + + return 0; +} + +static size_t _read(void *instance, uint8_t *bp, size_t n) { + + (void)instance; + (void)bp; + (void)n; + + return 0; +} + +static msg_t _put(void *instance, uint8_t b) { + + (void)instance; + (void)b; + + return FILE_OK; +} + +static msg_t _get(void *instance) { + + (void)instance; + + return FILE_OK; +} + +static msg_t _close(void *instance) { + + /* Close is not supported.*/ + (void)instance; + + return FILE_OK; +} + +static msg_t _geterror(void *instance) { + + (void)instance; + + return (msg_t)0; +} + +static msg_t _getsize(void *instance) { + + (void)instance; + + return 0; +} + +static msg_t _getposition(void *instance) { + + (void)instance; + + return 0; +} + +static msg_t _lseek(void *instance, fileoffset_t offset) { + + (void)instance; + (void)offset; + + return FILE_OK; +} + +/** + * @brief VMT for the RTC storage file interface. + */ +struct RTCDriverVMT _rtc_lld_vmt = { + _write, _read, _put, _get, + _close, _geterror, _getsize, _getposition, _lseek +}; +#endif /* RTC_HAS_STORAGE */ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Enable access to registers. + * + * @notapi + */ +void rtc_lld_init(void) { + + /* RTC object initialization.*/ + rtcObjectInit(&RTCD1); + + /* RTC pointer initialization.*/ + RTCD1.rtc = RTC; + + /* Disable write protection. */ + RTCD1.rtc->WPR = 0xCA; + RTCD1.rtc->WPR = 0x53; + + /* If calendar has not been initialized yet then proceed with the + initial setup.*/ + if (!(RTCD1.rtc->ISR & RTC_ISR_INITS)) { + + rtc_enter_init(); + + RTCD1.rtc->CR = 0; + RTCD1.rtc->ISR = RTC_ISR_INIT; /* Clearing all but RTC_ISR_INIT. */ + RTCD1.rtc->PRER = STM32_RTC_PRER_BITS; + RTCD1.rtc->PRER = STM32_RTC_PRER_BITS; + + rtc_exit_init(); + } + else + RTCD1.rtc->ISR &= ~RTC_ISR_RSF; +} + +/** + * @brief Set current time. + * @note Fractional part will be silently ignored. There is no possibility + * to set it on STM32 platform. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] timespec pointer to a @p RTCDateTime structure + * + * @notapi + */ +void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec) { + uint32_t dr, tr; + syssts_t sts; + + tr = rtc_encode_time(timespec); + dr = rtc_encode_date(timespec); + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + /* Writing the registers.*/ + rtc_enter_init(); + rtcp->rtc->TR = tr; + rtcp->rtc->DR = dr; + rtc_exit_init(); + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Get current time. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[out] timespec pointer to a @p RTCDateTime structure + * + * @notapi + */ +void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec) { + uint32_t dr, tr; + uint32_t subs; +#if STM32_RTC_HAS_SUBSECONDS + uint32_t ssr; +#endif /* STM32_RTC_HAS_SUBSECONDS */ + syssts_t sts; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + /* Synchronization with the RTC and reading the registers, note + DR must be read last.*/ + while ((rtcp->rtc->ISR & RTC_ISR_RSF) == 0) + ; +#if STM32_RTC_HAS_SUBSECONDS + ssr = rtcp->rtc->SSR; +#endif /* STM32_RTC_HAS_SUBSECONDS */ + tr = rtcp->rtc->TR; + dr = rtcp->rtc->DR; + rtcp->rtc->ISR &= ~RTC_ISR_RSF; + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); + + /* Decoding day time, this starts the atomic read sequence, see "Reading + the calendar" in the RTC documentation.*/ + rtc_decode_time(tr, timespec); + + /* If the RTC is capable of sub-second counting then the value is + normalized in milliseconds and added to the time.*/ +#if STM32_RTC_HAS_SUBSECONDS + subs = (((STM32_RTC_PRESS_VALUE - 1U) - ssr) * 1000U) / STM32_RTC_PRESS_VALUE; +#else + subs = 0; +#endif /* STM32_RTC_HAS_SUBSECONDS */ + timespec->millisecond += subs; + + /* Decoding date, this concludes the atomic read sequence.*/ + rtc_decode_date(dr, timespec); +} + +#if (RTC_ALARMS > 0) || defined(__DOXYGEN__) +/** + * @brief Set alarm time. + * @note Default value after BKP domain reset for both comparators is 0. + * @note Function does not performs any checks of alarm time validity. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure. + * @param[in] alarm alarm identifier. Can be 1 or 2. + * @param[in] alarmspec pointer to a @p RTCAlarm structure. + * + * @notapi + */ +void rtc_lld_set_alarm(RTCDriver *rtcp, + rtcalarm_t alarm, + const RTCAlarm *alarmspec) { + syssts_t sts; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + if (alarm == 0) { + if (alarmspec != NULL) { + rtcp->rtc->CR &= ~RTC_CR_ALRAE; + while (!(rtcp->rtc->ISR & RTC_ISR_ALRAWF)) + ; + rtcp->rtc->ALRMAR = alarmspec->alrmr; + rtcp->rtc->CR |= RTC_CR_ALRAE; + rtcp->rtc->CR |= RTC_CR_ALRAIE; + } + else { + rtcp->rtc->CR &= ~RTC_CR_ALRAIE; + rtcp->rtc->CR &= ~RTC_CR_ALRAE; + } + } +#if RTC_ALARMS > 1 + else { + if (alarmspec != NULL) { + rtcp->rtc->CR &= ~RTC_CR_ALRBE; + while (!(rtcp->rtc->ISR & RTC_ISR_ALRBWF)) + ; + rtcp->rtc->ALRMBR = alarmspec->alrmr; + rtcp->rtc->CR |= RTC_CR_ALRBE; + rtcp->rtc->CR |= RTC_CR_ALRBIE; + } + else { + rtcp->rtc->CR &= ~RTC_CR_ALRBIE; + rtcp->rtc->CR &= ~RTC_CR_ALRBE; + } + } +#endif /* RTC_ALARMS > 1 */ + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Get alarm time. + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] alarm alarm identifier + * @param[out] alarmspec pointer to a @p RTCAlarm structure + * + * @notapi + */ +void rtc_lld_get_alarm(RTCDriver *rtcp, + rtcalarm_t alarm, + RTCAlarm *alarmspec) { + + if (alarm == 0) + alarmspec->alrmr = rtcp->rtc->ALRMAR; +#if RTC_ALARMS > 1 + else + alarmspec->alrmr = rtcp->rtc->ALRMBR; +#endif /* RTC_ALARMS > 1 */ +} +#endif /* RTC_ALARMS > 0 */ + +#if STM32_RTC_HAS_PERIODIC_WAKEUPS || defined(__DOXYGEN__) +/** + * @brief Sets time of periodic wakeup. + * @note Default value after BKP domain reset is 0x0000FFFF + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[in] wakeupspec pointer to a @p RTCWakeup structure + * + * @api + */ +void rtcSTM32SetPeriodicWakeup(RTCDriver *rtcp, const RTCWakeup *wakeupspec) { + syssts_t sts; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + if (wakeupspec != NULL) { + osalDbgCheck(wakeupspec->wutr != 0x30000); + + rtcp->rtc->CR &= ~RTC_CR_WUTE; + while (!(rtcp->rtc->ISR & RTC_ISR_WUTWF)) + ; + rtcp->rtc->WUTR = wakeupspec->wutr & 0xFFFF; + rtcp->rtc->CR = (wakeupspec->wutr >> 16) & 0x7; + rtcp->rtc->CR |= RTC_CR_WUTIE; + rtcp->rtc->CR |= RTC_CR_WUTE; + } + else { + rtcp->rtc->CR &= ~RTC_CR_WUTIE; + rtcp->rtc->CR &= ~RTC_CR_WUTE; + } + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} + +/** + * @brief Gets time of periodic wakeup. + * @note Default value after BKP domain reset is 0x0000FFFF + * @note The function can be called from any context. + * + * @param[in] rtcp pointer to RTC driver structure + * @param[out] wakeupspec pointer to a @p RTCWakeup structure + * + * @api + */ +void rtcSTM32GetPeriodicWakeup(RTCDriver *rtcp, RTCWakeup *wakeupspec) { + syssts_t sts; + + /* Entering a reentrant critical zone.*/ + sts = osalSysGetStatusAndLockX(); + + wakeupspec->wutr = 0; + wakeupspec->wutr |= rtcp->rtc->WUTR; + wakeupspec->wutr |= (((uint32_t)rtcp->rtc->CR) & 0x7) << 16; + + /* Leaving a reentrant critical zone.*/ + osalSysRestoreStatusX(sts); +} +#endif /* STM32_RTC_HAS_PERIODIC_WAKEUPS */ + +#endif /* HAL_USE_RTC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.h b/os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.h new file mode 100644 index 000000000..bd0e620c5 --- /dev/null +++ b/os/hal/ports/STM32/LLD/RTCv2/hal_rtc_lld.h @@ -0,0 +1,237 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Uladzimir Pylinsky + aka barthess. + */ + +/** + * @file STM32/RTCv2/rtc_lld.h + * @brief STM32L1xx/STM32F2xx/STM32F4xx RTC low level driver header. + * + * @addtogroup RTC + * @{ + */ + +#ifndef _RTC_LLD_H_ +#define _RTC_LLD_H_ + +#if HAL_USE_RTC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Implementation capabilities + */ +/** + * @brief Callback support int the driver. + */ +#define RTC_SUPPORTS_CALLBACKS STM32_RTC_HAS_INTERRUPTS + +/** + * @brief Number of alarms available. + */ +#define RTC_ALARMS STM32_RTC_NUM_ALARMS + +/** + * @brief Presence of a local persistent storage. + */ +#define RTC_HAS_STORAGE FALSE +/** @} */ + +/** + * @brief RTC PRER register initializer. + */ +#define RTC_PRER(a, s) ((((a) - 1) << 16) | ((s) - 1)) + +/** + * @name Alarm helper macros + * @{ + */ +#define RTC_ALRM_MSK4 (1U << 31) +#define RTC_ALRM_WDSEL (1U << 30) +#define RTC_ALRM_DT(n) ((n) << 28) +#define RTC_ALRM_DU(n) ((n) << 24) +#define RTC_ALRM_MSK3 (1U << 23) +#define RTC_ALRM_HT(n) ((n) << 20) +#define RTC_ALRM_HU(n) ((n) << 16) +#define RTC_ALRM_MSK2 (1U << 15) +#define RTC_ALRM_MNT(n) ((n) << 12) +#define RTC_ALRM_MNU(n) ((n) << 8) +#define RTC_ALRM_MSK1 (1U << 7) +#define RTC_ALRM_ST(n) ((n) << 4) +#define RTC_ALRM_SU(n) ((n) << 0) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief RTC PRES register initialization. + * @note The default is calculated for a 32768Hz clock. + */ +#if !defined(STM32_RTC_PRESA_VALUE) || defined(__DOXYGEN__) +#define STM32_RTC_PRESA_VALUE 32 +#endif + +/** + * @brief RTC PRESS divider initialization. + * @note The default is calculated for a 32768Hz clock. + */ +#if !defined(STM32_RTC_PRESS_VALUE) || defined(__DOXYGEN__) +#define STM32_RTC_PRESS_VALUE 1024 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if HAL_USE_RTC && !STM32_HAS_RTC +#error "RTC not present in the selected device" +#endif + +#if !(STM32_RTCSEL == STM32_RTCSEL_LSE) && \ + !(STM32_RTCSEL == STM32_RTCSEL_LSI) && \ + !(STM32_RTCSEL == STM32_RTCSEL_HSEDIV) +#error "invalid source selected for RTC clock" +#endif + +#if STM32_PCLK1 < (STM32_RTCCLK * 7) +#error "STM32_PCLK1 frequency is too low" +#endif + +/** + * @brief Initialization for the RTC_PRER register. + */ +#define STM32_RTC_PRER_BITS RTC_PRER(STM32_RTC_PRESA_VALUE, \ + STM32_RTC_PRESS_VALUE) + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief FileStream specific methods. + */ +#define _rtc_driver_methods \ + _file_stream_methods + +/** + * @brief Type of an RTC alarm number. + */ +typedef uint32_t rtcalarm_t; + +/** + * @brief Type of a structure representing an RTC alarm time stamp. + */ +typedef struct { + /** + * @brief Type of an alarm as encoded in RTC ALRMxR registers. + */ + uint32_t alrmr; +} RTCAlarm; + +#if STM32_RTC_HAS_PERIODIC_WAKEUPS +/** + * @brief Type of a wakeup as encoded in RTC WUTR register. + */ +typedef struct { + /** + * @brief Wakeup as encoded in RTC WUTR register. + * @note ((WUTR == 0) || (WUCKSEL == 3)) are a forbidden combination. + */ + uint32_t wutr; +} RTCWakeup; +#endif + +#if RTC_HAS_STORAGE || defined(__DOXYGEN__) +/** + * @extends FileStream + * + * @brief @p RTCDriver virtual methods table. + */ +struct RTCDriverVMT { + _rtc_driver_methods +}; +#endif + +/** + * @brief Structure representing an RTC driver. + */ +struct RTCDriver { +#if RTC_HAS_STORAGE || defined(__DOXYGEN__) + /** + * @brief Virtual Methods Table. + */ + const struct RTCDriverVMT *vmt; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the RTC registers block. + */ + RTC_TypeDef *rtc; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern RTCDriver RTCD1; +#if RTC_HAS_STORAGE +extern struct RTCDriverVMT _rtc_lld_vmt; +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void rtc_lld_init(void); + void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec); + void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec); +#if RTC_ALARMS > 0 + void rtc_lld_set_alarm(RTCDriver *rtcp, + rtcalarm_t alarm, + const RTCAlarm *alarmspec); + void rtc_lld_get_alarm(RTCDriver *rtcp, + rtcalarm_t alarm, + RTCAlarm *alarmspec); +#endif +#if STM32_RTC_HAS_PERIODIC_WAKEUPS + void rtcSTM32SetPeriodicWakeup(RTCDriver *rtcp, const RTCWakeup *wakeupspec); + void rtcSTM32GetPeriodicWakeup(RTCDriver *rtcp, RTCWakeup *wakeupspec); +#endif /* STM32_RTC_HAS_PERIODIC_WAKEUPS */ +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_RTC */ + +#endif /* _RTC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv2/rtc_lld.c b/os/hal/ports/STM32/LLD/RTCv2/rtc_lld.c deleted file mode 100644 index ed78e121e..000000000 --- a/os/hal/ports/STM32/LLD/RTCv2/rtc_lld.c +++ /dev/null @@ -1,548 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/RTCv2/rtc_lld.c - * @brief STM32L1xx/STM32F2xx/STM32F4xx RTC low level driver. - * - * @addtogroup RTC - * @{ - */ - -#include "hal.h" - -#if HAL_USE_RTC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define RTC_TR_PM_OFFSET 22 -#define RTC_TR_HT_OFFSET 20 -#define RTC_TR_HU_OFFSET 16 -#define RTC_TR_MNT_OFFSET 12 -#define RTC_TR_MNU_OFFSET 8 -#define RTC_TR_ST_OFFSET 4 -#define RTC_TR_SU_OFFSET 0 - -#define RTC_DR_YT_OFFSET 20 -#define RTC_DR_YU_OFFSET 16 -#define RTC_DR_WDU_OFFSET 13 -#define RTC_DR_MT_OFFSET 12 -#define RTC_DR_MU_OFFSET 8 -#define RTC_DR_DT_OFFSET 4 -#define RTC_DR_DU_OFFSET 0 - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief RTC driver identifier. - */ -RTCDriver RTCD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Beginning of configuration procedure. - * - * @notapi - */ -static void rtc_enter_init(void) { - - RTCD1.rtc->ISR |= RTC_ISR_INIT; - while ((RTCD1.rtc->ISR & RTC_ISR_INITF) == 0) - ; -} - -/** - * @brief Finalizing of configuration procedure. - * - * @notapi - */ -static inline void rtc_exit_init(void) { - - RTCD1.rtc->ISR &= ~RTC_ISR_INIT; -} - -/** - * @brief Converts time from TR register encoding to timespec. - * - * @param[in] tr TR register value - * @param[out] timespec pointer to a @p RTCDateTime structure - * - * @notapi - */ -static void rtc_decode_time(uint32_t tr, RTCDateTime *timespec) { - uint32_t n; - - n = ((tr >> RTC_TR_HT_OFFSET) & 3) * 36000000; - n += ((tr >> RTC_TR_HU_OFFSET) & 15) * 3600000; - n += ((tr >> RTC_TR_MNT_OFFSET) & 7) * 600000; - n += ((tr >> RTC_TR_MNU_OFFSET) & 15) * 60000; - n += ((tr >> RTC_TR_ST_OFFSET) & 7) * 10000; - n += ((tr >> RTC_TR_SU_OFFSET) & 15) * 1000; - timespec->millisecond = n; -} - -/** - * @brief Converts date from DR register encoding to timespec. - * - * @param[in] dr DR register value - * @param[out] timespec pointer to a @p RTCDateTime structure - * - * @notapi - */ -static void rtc_decode_date(uint32_t dr, RTCDateTime *timespec) { - - timespec->year = (((dr >> RTC_DR_YT_OFFSET) & 15) * 10) + - ((dr >> RTC_DR_YU_OFFSET) & 15); - timespec->month = (((dr >> RTC_TR_MNT_OFFSET) & 1) * 10) + - ((dr >> RTC_TR_MNU_OFFSET) & 15); - timespec->day = (((dr >> RTC_DR_DT_OFFSET) & 3) * 10) + - ((dr >> RTC_DR_DU_OFFSET) & 15); - timespec->dayofweek = (dr >> RTC_DR_WDU_OFFSET) & 7; -} - -/** - * @brief Converts time from timespec to TR register encoding. - * - * @param[in] timespec pointer to a @p RTCDateTime structure - * @return the TR register encoding. - * - * @notapi - */ -static uint32_t rtc_encode_time(const RTCDateTime *timespec) { - uint32_t n, tr = 0; - - /* Subseconds cannot be set.*/ - n = timespec->millisecond / 1000; - - /* Seconds conversion.*/ - tr = tr | ((n % 10) << RTC_TR_SU_OFFSET); - n /= 10; - tr = tr | ((n % 6) << RTC_TR_ST_OFFSET); - n /= 6; - - /* Minutes conversion.*/ - tr = tr | ((n % 10) << RTC_TR_MNU_OFFSET); - n /= 10; - tr = tr | ((n % 6) << RTC_TR_MNT_OFFSET); - n /= 6; - - /* Hours conversion.*/ - tr = tr | ((n % 10) << RTC_TR_HU_OFFSET); - n /= 10; - tr = tr | (n << RTC_TR_HT_OFFSET); - - return tr; -} - -/** - * @brief Converts a date from timespec to DR register encoding. - * - * @param[in] timespec pointer to a @p RTCDateTime structure - * @return the DR register encoding. - * - * @notapi - */ -static uint32_t rtc_encode_date(const RTCDateTime *timespec) { - uint32_t n, dr = 0; - - /* Year conversion. Note, only years last two digits are considered.*/ - n = timespec->year; - dr = dr | ((n % 10) << RTC_DR_YU_OFFSET); - n /= 10; - dr = dr | ((n % 10) << RTC_DR_YT_OFFSET); - - /* Months conversion.*/ - n = timespec->month; - dr = dr | ((n % 10) << RTC_DR_MU_OFFSET); - n /= 10; - dr = dr | ((n % 10) << RTC_DR_MT_OFFSET); - - /* Days conversion.*/ - n = timespec->day; - dr = dr | ((n % 10) << RTC_DR_DU_OFFSET); - n /= 10; - dr = dr | ((n % 10) << RTC_DR_DT_OFFSET); - - /* Days of week conversion.*/ - dr = dr | (timespec->dayofweek << RTC_DR_WDU_OFFSET); - - return dr; -} - -#if RTC_HAS_STORAGE -/* TODO: Map on the backup SRAM on devices that have it.*/ -static size_t _write(void *instance, const uint8_t *bp, size_t n) { - - (void)instance; - (void)bp; - (void)n; - - return 0; -} - -static size_t _read(void *instance, uint8_t *bp, size_t n) { - - (void)instance; - (void)bp; - (void)n; - - return 0; -} - -static msg_t _put(void *instance, uint8_t b) { - - (void)instance; - (void)b; - - return FILE_OK; -} - -static msg_t _get(void *instance) { - - (void)instance; - - return FILE_OK; -} - -static msg_t _close(void *instance) { - - /* Close is not supported.*/ - (void)instance; - - return FILE_OK; -} - -static msg_t _geterror(void *instance) { - - (void)instance; - - return (msg_t)0; -} - -static msg_t _getsize(void *instance) { - - (void)instance; - - return 0; -} - -static msg_t _getposition(void *instance) { - - (void)instance; - - return 0; -} - -static msg_t _lseek(void *instance, fileoffset_t offset) { - - (void)instance; - (void)offset; - - return FILE_OK; -} - -/** - * @brief VMT for the RTC storage file interface. - */ -struct RTCDriverVMT _rtc_lld_vmt = { - _write, _read, _put, _get, - _close, _geterror, _getsize, _getposition, _lseek -}; -#endif /* RTC_HAS_STORAGE */ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Enable access to registers. - * - * @notapi - */ -void rtc_lld_init(void) { - - /* RTC object initialization.*/ - rtcObjectInit(&RTCD1); - - /* RTC pointer initialization.*/ - RTCD1.rtc = RTC; - - /* Disable write protection. */ - RTCD1.rtc->WPR = 0xCA; - RTCD1.rtc->WPR = 0x53; - - /* If calendar has not been initialized yet then proceed with the - initial setup.*/ - if (!(RTCD1.rtc->ISR & RTC_ISR_INITS)) { - - rtc_enter_init(); - - RTCD1.rtc->CR = 0; - RTCD1.rtc->ISR = RTC_ISR_INIT; /* Clearing all but RTC_ISR_INIT. */ - RTCD1.rtc->PRER = STM32_RTC_PRER_BITS; - RTCD1.rtc->PRER = STM32_RTC_PRER_BITS; - - rtc_exit_init(); - } - else - RTCD1.rtc->ISR &= ~RTC_ISR_RSF; -} - -/** - * @brief Set current time. - * @note Fractional part will be silently ignored. There is no possibility - * to set it on STM32 platform. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] timespec pointer to a @p RTCDateTime structure - * - * @notapi - */ -void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec) { - uint32_t dr, tr; - syssts_t sts; - - tr = rtc_encode_time(timespec); - dr = rtc_encode_date(timespec); - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - /* Writing the registers.*/ - rtc_enter_init(); - rtcp->rtc->TR = tr; - rtcp->rtc->DR = dr; - rtc_exit_init(); - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Get current time. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[out] timespec pointer to a @p RTCDateTime structure - * - * @notapi - */ -void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec) { - uint32_t dr, tr; - uint32_t subs; -#if STM32_RTC_HAS_SUBSECONDS - uint32_t ssr; -#endif /* STM32_RTC_HAS_SUBSECONDS */ - syssts_t sts; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - /* Synchronization with the RTC and reading the registers, note - DR must be read last.*/ - while ((rtcp->rtc->ISR & RTC_ISR_RSF) == 0) - ; -#if STM32_RTC_HAS_SUBSECONDS - ssr = rtcp->rtc->SSR; -#endif /* STM32_RTC_HAS_SUBSECONDS */ - tr = rtcp->rtc->TR; - dr = rtcp->rtc->DR; - rtcp->rtc->ISR &= ~RTC_ISR_RSF; - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); - - /* Decoding day time, this starts the atomic read sequence, see "Reading - the calendar" in the RTC documentation.*/ - rtc_decode_time(tr, timespec); - - /* If the RTC is capable of sub-second counting then the value is - normalized in milliseconds and added to the time.*/ -#if STM32_RTC_HAS_SUBSECONDS - subs = (((STM32_RTC_PRESS_VALUE - 1U) - ssr) * 1000U) / STM32_RTC_PRESS_VALUE; -#else - subs = 0; -#endif /* STM32_RTC_HAS_SUBSECONDS */ - timespec->millisecond += subs; - - /* Decoding date, this concludes the atomic read sequence.*/ - rtc_decode_date(dr, timespec); -} - -#if (RTC_ALARMS > 0) || defined(__DOXYGEN__) -/** - * @brief Set alarm time. - * @note Default value after BKP domain reset for both comparators is 0. - * @note Function does not performs any checks of alarm time validity. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure. - * @param[in] alarm alarm identifier. Can be 1 or 2. - * @param[in] alarmspec pointer to a @p RTCAlarm structure. - * - * @notapi - */ -void rtc_lld_set_alarm(RTCDriver *rtcp, - rtcalarm_t alarm, - const RTCAlarm *alarmspec) { - syssts_t sts; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - if (alarm == 0) { - if (alarmspec != NULL) { - rtcp->rtc->CR &= ~RTC_CR_ALRAE; - while (!(rtcp->rtc->ISR & RTC_ISR_ALRAWF)) - ; - rtcp->rtc->ALRMAR = alarmspec->alrmr; - rtcp->rtc->CR |= RTC_CR_ALRAE; - rtcp->rtc->CR |= RTC_CR_ALRAIE; - } - else { - rtcp->rtc->CR &= ~RTC_CR_ALRAIE; - rtcp->rtc->CR &= ~RTC_CR_ALRAE; - } - } -#if RTC_ALARMS > 1 - else { - if (alarmspec != NULL) { - rtcp->rtc->CR &= ~RTC_CR_ALRBE; - while (!(rtcp->rtc->ISR & RTC_ISR_ALRBWF)) - ; - rtcp->rtc->ALRMBR = alarmspec->alrmr; - rtcp->rtc->CR |= RTC_CR_ALRBE; - rtcp->rtc->CR |= RTC_CR_ALRBIE; - } - else { - rtcp->rtc->CR &= ~RTC_CR_ALRBIE; - rtcp->rtc->CR &= ~RTC_CR_ALRBE; - } - } -#endif /* RTC_ALARMS > 1 */ - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Get alarm time. - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] alarm alarm identifier - * @param[out] alarmspec pointer to a @p RTCAlarm structure - * - * @notapi - */ -void rtc_lld_get_alarm(RTCDriver *rtcp, - rtcalarm_t alarm, - RTCAlarm *alarmspec) { - - if (alarm == 0) - alarmspec->alrmr = rtcp->rtc->ALRMAR; -#if RTC_ALARMS > 1 - else - alarmspec->alrmr = rtcp->rtc->ALRMBR; -#endif /* RTC_ALARMS > 1 */ -} -#endif /* RTC_ALARMS > 0 */ - -#if STM32_RTC_HAS_PERIODIC_WAKEUPS || defined(__DOXYGEN__) -/** - * @brief Sets time of periodic wakeup. - * @note Default value after BKP domain reset is 0x0000FFFF - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[in] wakeupspec pointer to a @p RTCWakeup structure - * - * @api - */ -void rtcSTM32SetPeriodicWakeup(RTCDriver *rtcp, const RTCWakeup *wakeupspec) { - syssts_t sts; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - if (wakeupspec != NULL) { - osalDbgCheck(wakeupspec->wutr != 0x30000); - - rtcp->rtc->CR &= ~RTC_CR_WUTE; - while (!(rtcp->rtc->ISR & RTC_ISR_WUTWF)) - ; - rtcp->rtc->WUTR = wakeupspec->wutr & 0xFFFF; - rtcp->rtc->CR = (wakeupspec->wutr >> 16) & 0x7; - rtcp->rtc->CR |= RTC_CR_WUTIE; - rtcp->rtc->CR |= RTC_CR_WUTE; - } - else { - rtcp->rtc->CR &= ~RTC_CR_WUTIE; - rtcp->rtc->CR &= ~RTC_CR_WUTE; - } - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} - -/** - * @brief Gets time of periodic wakeup. - * @note Default value after BKP domain reset is 0x0000FFFF - * @note The function can be called from any context. - * - * @param[in] rtcp pointer to RTC driver structure - * @param[out] wakeupspec pointer to a @p RTCWakeup structure - * - * @api - */ -void rtcSTM32GetPeriodicWakeup(RTCDriver *rtcp, RTCWakeup *wakeupspec) { - syssts_t sts; - - /* Entering a reentrant critical zone.*/ - sts = osalSysGetStatusAndLockX(); - - wakeupspec->wutr = 0; - wakeupspec->wutr |= rtcp->rtc->WUTR; - wakeupspec->wutr |= (((uint32_t)rtcp->rtc->CR) & 0x7) << 16; - - /* Leaving a reentrant critical zone.*/ - osalSysRestoreStatusX(sts); -} -#endif /* STM32_RTC_HAS_PERIODIC_WAKEUPS */ - -#endif /* HAL_USE_RTC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/RTCv2/rtc_lld.h b/os/hal/ports/STM32/LLD/RTCv2/rtc_lld.h deleted file mode 100644 index bd0e620c5..000000000 --- a/os/hal/ports/STM32/LLD/RTCv2/rtc_lld.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Uladzimir Pylinsky - aka barthess. - */ - -/** - * @file STM32/RTCv2/rtc_lld.h - * @brief STM32L1xx/STM32F2xx/STM32F4xx RTC low level driver header. - * - * @addtogroup RTC - * @{ - */ - -#ifndef _RTC_LLD_H_ -#define _RTC_LLD_H_ - -#if HAL_USE_RTC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Implementation capabilities - */ -/** - * @brief Callback support int the driver. - */ -#define RTC_SUPPORTS_CALLBACKS STM32_RTC_HAS_INTERRUPTS - -/** - * @brief Number of alarms available. - */ -#define RTC_ALARMS STM32_RTC_NUM_ALARMS - -/** - * @brief Presence of a local persistent storage. - */ -#define RTC_HAS_STORAGE FALSE -/** @} */ - -/** - * @brief RTC PRER register initializer. - */ -#define RTC_PRER(a, s) ((((a) - 1) << 16) | ((s) - 1)) - -/** - * @name Alarm helper macros - * @{ - */ -#define RTC_ALRM_MSK4 (1U << 31) -#define RTC_ALRM_WDSEL (1U << 30) -#define RTC_ALRM_DT(n) ((n) << 28) -#define RTC_ALRM_DU(n) ((n) << 24) -#define RTC_ALRM_MSK3 (1U << 23) -#define RTC_ALRM_HT(n) ((n) << 20) -#define RTC_ALRM_HU(n) ((n) << 16) -#define RTC_ALRM_MSK2 (1U << 15) -#define RTC_ALRM_MNT(n) ((n) << 12) -#define RTC_ALRM_MNU(n) ((n) << 8) -#define RTC_ALRM_MSK1 (1U << 7) -#define RTC_ALRM_ST(n) ((n) << 4) -#define RTC_ALRM_SU(n) ((n) << 0) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief RTC PRES register initialization. - * @note The default is calculated for a 32768Hz clock. - */ -#if !defined(STM32_RTC_PRESA_VALUE) || defined(__DOXYGEN__) -#define STM32_RTC_PRESA_VALUE 32 -#endif - -/** - * @brief RTC PRESS divider initialization. - * @note The default is calculated for a 32768Hz clock. - */ -#if !defined(STM32_RTC_PRESS_VALUE) || defined(__DOXYGEN__) -#define STM32_RTC_PRESS_VALUE 1024 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if HAL_USE_RTC && !STM32_HAS_RTC -#error "RTC not present in the selected device" -#endif - -#if !(STM32_RTCSEL == STM32_RTCSEL_LSE) && \ - !(STM32_RTCSEL == STM32_RTCSEL_LSI) && \ - !(STM32_RTCSEL == STM32_RTCSEL_HSEDIV) -#error "invalid source selected for RTC clock" -#endif - -#if STM32_PCLK1 < (STM32_RTCCLK * 7) -#error "STM32_PCLK1 frequency is too low" -#endif - -/** - * @brief Initialization for the RTC_PRER register. - */ -#define STM32_RTC_PRER_BITS RTC_PRER(STM32_RTC_PRESA_VALUE, \ - STM32_RTC_PRESS_VALUE) - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief FileStream specific methods. - */ -#define _rtc_driver_methods \ - _file_stream_methods - -/** - * @brief Type of an RTC alarm number. - */ -typedef uint32_t rtcalarm_t; - -/** - * @brief Type of a structure representing an RTC alarm time stamp. - */ -typedef struct { - /** - * @brief Type of an alarm as encoded in RTC ALRMxR registers. - */ - uint32_t alrmr; -} RTCAlarm; - -#if STM32_RTC_HAS_PERIODIC_WAKEUPS -/** - * @brief Type of a wakeup as encoded in RTC WUTR register. - */ -typedef struct { - /** - * @brief Wakeup as encoded in RTC WUTR register. - * @note ((WUTR == 0) || (WUCKSEL == 3)) are a forbidden combination. - */ - uint32_t wutr; -} RTCWakeup; -#endif - -#if RTC_HAS_STORAGE || defined(__DOXYGEN__) -/** - * @extends FileStream - * - * @brief @p RTCDriver virtual methods table. - */ -struct RTCDriverVMT { - _rtc_driver_methods -}; -#endif - -/** - * @brief Structure representing an RTC driver. - */ -struct RTCDriver { -#if RTC_HAS_STORAGE || defined(__DOXYGEN__) - /** - * @brief Virtual Methods Table. - */ - const struct RTCDriverVMT *vmt; -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the RTC registers block. - */ - RTC_TypeDef *rtc; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern RTCDriver RTCD1; -#if RTC_HAS_STORAGE -extern struct RTCDriverVMT _rtc_lld_vmt; -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void rtc_lld_init(void); - void rtc_lld_set_time(RTCDriver *rtcp, const RTCDateTime *timespec); - void rtc_lld_get_time(RTCDriver *rtcp, RTCDateTime *timespec); -#if RTC_ALARMS > 0 - void rtc_lld_set_alarm(RTCDriver *rtcp, - rtcalarm_t alarm, - const RTCAlarm *alarmspec); - void rtc_lld_get_alarm(RTCDriver *rtcp, - rtcalarm_t alarm, - RTCAlarm *alarmspec); -#endif -#if STM32_RTC_HAS_PERIODIC_WAKEUPS - void rtcSTM32SetPeriodicWakeup(RTCDriver *rtcp, const RTCWakeup *wakeupspec); - void rtcSTM32GetPeriodicWakeup(RTCDriver *rtcp, RTCWakeup *wakeupspec); -#endif /* STM32_RTC_HAS_PERIODIC_WAKEUPS */ -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_RTC */ - -#endif /* _RTC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.c b/os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.c new file mode 100644 index 000000000..2519b11b2 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.c @@ -0,0 +1,873 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SDIOv1/sdc_lld.c + * @brief STM32 SDC subsystem low level driver source. + * + * @addtogroup SDC + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_SDC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \ + STM32_SDC_SDIO_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief SDCD1 driver identifier.*/ +SDCDriver SDCD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if STM32_SDC_SDIO_UNALIGNED_SUPPORT +/** + * @brief Buffer for temporary storage during unaligned transfers. + */ +static union { + uint32_t alignment; + uint8_t buf[MMCSD_BLOCK_SIZE]; +} u; +#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ + + +/** + * @brief SDIO default configuration. + */ +static const SDCConfig sdc_default_cfg = { + NULL, + SDC_MODE_4BIT +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Prepares to handle read transaction. + * @details Designed for read special registers from card. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] buf pointer to the read buffer + * @param[in] bytes number of bytes to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_read_bytes(SDCDriver *sdcp, + uint8_t *buf, uint32_t bytes) { + osalDbgCheck(bytes < 0x1000000); + + sdcp->sdio->DTIMER = STM32_SDC_READ_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for reading.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, bytes / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; + sdcp->sdio->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_RXOVERRIE | + SDIO_MASK_DATAENDIE; + sdcp->sdio->DLEN = bytes; + + /* Transaction starts just after DTEN bit setting.*/ + sdcp->sdio->DCTRL = SDIO_DCTRL_DTDIR | + SDIO_DCTRL_DTMODE | /* multibyte data transfer */ + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + + return HAL_SUCCESS; +} + +/** + * @brief Prepares card to handle read transaction. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[in] n number of blocks to read + * @param[in] resp pointer to the response buffer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, + uint32_t n, uint32_t *resp) { + + /* Driver handles data in 512 bytes blocks (just like HC cards). But if we + have not HC card than we must convert address from blocks to bytes.*/ + if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) + startblk *= MMCSD_BLOCK_SIZE; + + if (n > 1) { + /* Send read multiple blocks command to card.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + else{ + /* Send read single block command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + + return HAL_SUCCESS; +} + +/** + * @brief Prepares card to handle write transaction. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[in] n number of blocks to write + * @param[in] resp pointer to the response buffer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk, + uint32_t n, uint32_t *resp) { + + /* Driver handles data in 512 bytes blocks (just like HC cards). But if we + have not HC card than we must convert address from blocks to bytes.*/ + if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) + startblk *= MMCSD_BLOCK_SIZE; + + if (n > 1) { + /* Write multiple blocks command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + else{ + /* Write single block command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + + return HAL_SUCCESS; +} + +/** + * @brief Wait end of data transaction and performs finalizations. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] n number of blocks in transaction + * @param[in] resp pointer to the response buffer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + */ +static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, + uint32_t *resp) { + + /* Note the mask is checked before going to sleep because the interrupt + may have occurred before reaching the critical zone.*/ + osalSysLock(); + if (sdcp->sdio->MASK != 0) + osalThreadSuspendS(&sdcp->thread); + if ((sdcp->sdio->STA & SDIO_STA_DATAEND) == 0) { + osalSysUnlock(); + return HAL_FAILED; + } + +#if (defined(STM32F4XX) || defined(STM32F2XX)) + /* Wait until DMA channel enabled to be sure that all data transferred.*/ + while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) + ; + + /* DMA event flags must be manually cleared.*/ + dmaStreamClearInterrupt(sdcp->dma); + + sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; + sdcp->sdio->DCTRL = 0; + osalSysUnlock(); + + /* Wait until interrupt flags to be cleared.*/ + /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) + dmaStreamClearInterrupt(sdcp->dma);*/ +#else + /* Waits for transfer completion at DMA level, then the stream is + disabled and cleared.*/ + dmaWaitCompletion(sdcp->dma); + + sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; + sdcp->sdio->DCTRL = 0; + osalSysUnlock(); +#endif + + /* Finalize transaction.*/ + if (n > 1) + return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); + + return HAL_SUCCESS; +} + +/** + * @brief Gets SDC errors. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] sta value of the STA register + * + * @notapi + */ +static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) { + uint32_t errors = SDC_NO_ERROR; + + if (sta & SDIO_STA_CCRCFAIL) + errors |= SDC_CMD_CRC_ERROR; + if (sta & SDIO_STA_DCRCFAIL) + errors |= SDC_DATA_CRC_ERROR; + if (sta & SDIO_STA_CTIMEOUT) + errors |= SDC_COMMAND_TIMEOUT; + if (sta & SDIO_STA_DTIMEOUT) + errors |= SDC_DATA_TIMEOUT; + if (sta & SDIO_STA_TXUNDERR) + errors |= SDC_TX_UNDERRUN; + if (sta & SDIO_STA_RXOVERR) + errors |= SDC_RX_OVERRUN; + if (sta & SDIO_STA_STBITERR) + errors |= SDC_STARTBIT_ERROR; + + sdcp->errors |= errors; +} + +/** + * @brief Performs clean transaction stopping in case of errors. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] n number of blocks in transaction + * @param[in] resp pointer to the response buffer + * + * @notapi + */ +static void sdc_lld_error_cleanup(SDCDriver *sdcp, + uint32_t n, + uint32_t *resp) { + uint32_t sta = sdcp->sdio->STA; + + dmaStreamClearInterrupt(sdcp->dma); + dmaStreamDisable(sdcp->dma); + sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; + sdcp->sdio->MASK = 0; + sdcp->sdio->DCTRL = 0; + sdc_lld_collect_errors(sdcp, sta); + if (n > 1) + sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if !defined(STM32_SDIO_HANDLER) +#error "STM32_SDIO_HANDLER not defined" +#endif +/** + * @brief SDIO IRQ handler. + * @details It just wakes transaction thread. All error handling performs in + * that thread. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_SDIO_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + osalSysLockFromISR(); + + /* Disables the source but the status flags are not reset because the + read/write functions needs to check them.*/ + SDIO->MASK = 0; + + osalThreadResumeI(&SDCD1.thread, MSG_OK); + + osalSysUnlockFromISR(); + + OSAL_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SDC driver initialization. + * + * @notapi + */ +void sdc_lld_init(void) { + + sdcObjectInit(&SDCD1); + SDCD1.thread = NULL; + SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM); + SDCD1.sdio = SDIO; +} + +/** + * @brief Configures and activates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start(SDCDriver *sdcp) { + + /* Checking configuration, using a default if NULL has been passed.*/ + if (sdcp->config == NULL) { + sdcp->config = &sdc_default_cfg; + } + + sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_WORD | + STM32_DMA_CR_MSIZE_WORD | + STM32_DMA_CR_MINC; + +#if (defined(STM32F4XX) || defined(STM32F2XX)) + sdcp->dmamode |= STM32_DMA_CR_PFCTRL | + STM32_DMA_CR_PBURST_INCR4 | + STM32_DMA_CR_MBURST_INCR4; +#endif + + if (sdcp->state == BLK_STOP) { + /* Note, the DMA must be enabled before the IRQs.*/ + bool b; + b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL); + osalDbgAssert(!b, "stream already allocated"); + dmaStreamSetPeripheral(sdcp->dma, &sdcp->sdio->FIFO); +#if (defined(STM32F4XX) || defined(STM32F2XX)) + dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); +#endif + nvicEnableVector(STM32_SDIO_NUMBER, STM32_SDC_SDIO_IRQ_PRIORITY); + rccEnableSDIO(FALSE); + } + + /* Configuration, card clock is initially stopped.*/ + sdcp->sdio->POWER = 0; + sdcp->sdio->CLKCR = 0; + sdcp->sdio->DCTRL = 0; + sdcp->sdio->DTIMER = 0; +} + +/** + * @brief Deactivates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop(SDCDriver *sdcp) { + + if (sdcp->state != BLK_STOP) { + + /* SDIO deactivation.*/ + sdcp->sdio->POWER = 0; + sdcp->sdio->CLKCR = 0; + sdcp->sdio->DCTRL = 0; + sdcp->sdio->DTIMER = 0; + + /* Clock deactivation.*/ + nvicDisableVector(STM32_SDIO_NUMBER); + dmaStreamRelease(sdcp->dma); + rccDisableSDIO(FALSE); + } +} + +/** + * @brief Starts the SDIO clock and sets it to init mode (400kHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start_clk(SDCDriver *sdcp) { + + /* Initial clock setting: 400kHz, 1bit mode.*/ + sdcp->sdio->CLKCR = STM32_SDIO_DIV_LS; + sdcp->sdio->POWER |= SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1; + sdcp->sdio->CLKCR |= SDIO_CLKCR_CLKEN; + + /* Clock activation delay.*/ + osalThreadSleep(OSAL_MS2ST(STM32_SDC_CLOCK_ACTIVATION_DELAY)); +} + +/** + * @brief Sets the SDIO clock to data mode (25MHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] clk the clock mode + * + * @notapi + */ +void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { +#if 0 + if (SDC_CLK_50MHz == clk) { + sdcp->sdio->CLKCR = (sdcp->sdio->CLKCR & 0xFFFFFF00U) | STM32_SDIO_DIV_HS + | SDIO_CLKCR_BYPASS; + } + else + sdcp->sdio->CLKCR = (sdcp->sdio->CLKCR & 0xFFFFFF00U) | STM32_SDIO_DIV_HS; +#else + (void)clk; + + sdcp->sdio->CLKCR = (sdcp->sdio->CLKCR & 0xFFFFFF00U) | STM32_SDIO_DIV_HS; +#endif +} + +/** + * @brief Stops the SDIO clock. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop_clk(SDCDriver *sdcp) { + + sdcp->sdio->CLKCR = 0; + sdcp->sdio->POWER = 0; +} + +/** + * @brief Switches the bus to 4 bits mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] mode bus mode + * + * @notapi + */ +void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { + uint32_t clk = sdcp->sdio->CLKCR & ~SDIO_CLKCR_WIDBUS; + + switch (mode) { + case SDC_MODE_1BIT: + sdcp->sdio->CLKCR = clk; + break; + case SDC_MODE_4BIT: + sdcp->sdio->CLKCR = clk | SDIO_CLKCR_WIDBUS_0; + break; + case SDC_MODE_8BIT: + sdcp->sdio->CLKCR = clk | SDIO_CLKCR_WIDBUS_1; + break; + } +} + +/** + * @brief Sends an SDIO command with no response expected. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * + * @notapi + */ +void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { + + sdcp->sdio->ARG = arg; + sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_CPSMEN; + while ((sdcp->sdio->STA & SDIO_STA_CMDSENT) == 0) + ; + sdcp->sdio->ICR = SDIO_ICR_CMDSENTC; +} + +/** + * @brief Sends an SDIO command with a short response expected. + * @note The CRC is not verified. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (one word) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + sdcp->sdio->ARG = arg; + sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; + while (((sta = sdcp->sdio->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL)) == 0) + ; + sdcp->sdio->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL); + if ((sta & (SDIO_STA_CTIMEOUT)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return HAL_FAILED; + } + *resp = sdcp->sdio->RESP1; + return HAL_SUCCESS; +} + +/** + * @brief Sends an SDIO command with a short response expected and CRC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (one word) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + sdcp->sdio->ARG = arg; + sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; + while (((sta = sdcp->sdio->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL)) == 0) + ; + sdcp->sdio->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL); + if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return HAL_FAILED; + } + *resp = sdcp->sdio->RESP1; + return HAL_SUCCESS; +} + +/** + * @brief Sends an SDIO command with a long response expected and CRC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (four words) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + (void)sdcp; + + sdcp->sdio->ARG = arg; + sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_WAITRESP_1 | + SDIO_CMD_CPSMEN; + while (((sta = sdcp->sdio->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL)) == 0) + ; + sdcp->sdio->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | + SDIO_STA_CCRCFAIL); + if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return HAL_FAILED; + } + /* Save bytes in reverse order because MSB in response comes first.*/ + *resp++ = sdcp->sdio->RESP4; + *resp++ = sdcp->sdio->RESP3; + *resp++ = sdcp->sdio->RESP2; + *resp = sdcp->sdio->RESP1; + return HAL_SUCCESS; +} + +/** + * @brief Reads special registers using data bus. + * @details Needs only during card detection procedure. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] buf pointer to the read buffer + * @param[in] bytes number of bytes to read + * @param[in] cmd card command + * @param[in] arg argument for command + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t arg) { + uint32_t resp[1]; + + if(sdc_lld_prepare_read_bytes(sdcp, buf, bytes)) + goto error; + + if (sdc_lld_send_cmd_short_crc(sdcp, cmd, arg, resp) + || MMCSD_R1_ERROR(resp[0])) + goto error; + + if (sdc_lld_wait_transaction_end(sdcp, 1, resp)) + goto error; + + return HAL_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, 1, resp); + return HAL_FAILED; +} + +/** + * @brief Reads one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[out] buf pointer to the read buffer + * @param[in] blocks number of blocks to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t blocks) { + uint32_t resp[1]; + + osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); + + sdcp->sdio->DTIMER = STM32_SDC_READ_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for reading.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; + sdcp->sdio->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_RXOVERRIE | + SDIO_MASK_DATAENDIE; + sdcp->sdio->DLEN = blocks * MMCSD_BLOCK_SIZE; + + /* Transaction starts just after DTEN bit setting.*/ + sdcp->sdio->DCTRL = SDIO_DCTRL_DTDIR | + SDIO_DCTRL_DBLOCKSIZE_3 | + SDIO_DCTRL_DBLOCKSIZE_0 | + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + + if (sdc_lld_prepare_read(sdcp, startblk, blocks, resp) == TRUE) + goto error; + + if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) + goto error; + + return HAL_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, blocks, resp); + return HAL_FAILED; +} + +/** + * @brief Writes one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to write + * @param[out] buf pointer to the write buffer + * @param[in] n number of blocks to write + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t blocks) { + uint32_t resp[1]; + + osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); + + sdcp->sdio->DTIMER = STM32_SDC_WRITE_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for writing.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; + sdcp->sdio->MASK = SDIO_MASK_DCRCFAILIE | + SDIO_MASK_DTIMEOUTIE | + SDIO_MASK_STBITERRIE | + SDIO_MASK_TXUNDERRIE | + SDIO_MASK_DATAENDIE; + sdcp->sdio->DLEN = blocks * MMCSD_BLOCK_SIZE; + + /* Talk to card what we want from it.*/ + if (sdc_lld_prepare_write(sdcp, startblk, blocks, resp) == TRUE) + goto error; + + /* Transaction starts just after DTEN bit setting.*/ + sdcp->sdio->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | + SDIO_DCTRL_DBLOCKSIZE_0 | + SDIO_DCTRL_DMAEN | + SDIO_DCTRL_DTEN; + + if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) + goto error; + + return HAL_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, blocks, resp); + return HAL_FAILED; +} + +/** + * @brief Reads one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[out] buf pointer to the read buffer + * @param[in] blocks number of blocks to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t blocks) { + +#if STM32_SDC_SDIO_UNALIGNED_SUPPORT + if (((unsigned)buf & 3) != 0) { + uint32_t i; + for (i = 0; i < blocks; i++) { + if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) + return HAL_FAILED; + memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); + buf += MMCSD_BLOCK_SIZE; + startblk++; + } + return HAL_SUCCESS; + } +#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ + return sdc_lld_read_aligned(sdcp, startblk, buf, blocks); +} + +/** + * @brief Writes one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to write + * @param[out] buf pointer to the write buffer + * @param[in] blocks number of blocks to write + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t blocks) { + +#if STM32_SDC_SDIO_UNALIGNED_SUPPORT + if (((unsigned)buf & 3) != 0) { + uint32_t i; + for (i = 0; i < blocks; i++) { + memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); + buf += MMCSD_BLOCK_SIZE; + if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) + return HAL_FAILED; + startblk++; + } + return HAL_SUCCESS; + } +#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ + return sdc_lld_write_aligned(sdcp, startblk, buf, blocks); +} + +/** + * @brief Waits for card idle condition. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS the operation succeeded. + * @retval HAL_FAILED the operation failed. + * + * @api + */ +bool sdc_lld_sync(SDCDriver *sdcp) { + + /* TODO: Implement.*/ + (void)sdcp; + return HAL_SUCCESS; +} + +#endif /* HAL_USE_SDC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.h b/os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.h new file mode 100644 index 000000000..baf1e9c61 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SDIOv1/hal_sdc_lld.h @@ -0,0 +1,330 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SDIOv1/sdc_lld.h + * @brief STM32 SDC subsystem low level driver header. + * + * @addtogroup SDC + * @{ + */ + +#ifndef _SDC_LLD_H_ +#define _SDC_LLD_H_ + +#if HAL_USE_SDC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Value to clear all interrupts flag at once. + */ +#define STM32_SDIO_ICR_ALL_FLAGS (SDIO_ICR_CCRCFAILC | SDIO_ICR_DCRCFAILC | \ + SDIO_ICR_CTIMEOUTC | SDIO_ICR_DTIMEOUTC | \ + SDIO_ICR_TXUNDERRC | SDIO_ICR_RXOVERRC | \ + SDIO_ICR_CMDRENDC | SDIO_ICR_CMDSENTC | \ + SDIO_ICR_DATAENDC | SDIO_ICR_STBITERRC | \ + SDIO_ICR_DBCKENDC | SDIO_ICR_SDIOITC | \ + SDIO_ICR_CEATAENDC) + +/** + * @brief Mask of error flags in STA register. + */ +#define STM32_SDIO_STA_ERROR_MASK (SDIO_STA_CCRCFAIL | SDIO_STA_DCRCFAIL | \ + SDIO_STA_CTIMEOUT | SDIO_STA_DTIMEOUT | \ + SDIO_STA_TXUNDERR | SDIO_STA_RXOVERR) + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SDIO DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_SDC_SDIO_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SDC_SDIO_DMA_PRIORITY 3 +#endif + +/** + * @brief SDIO interrupt priority level setting. + */ +#if !defined(STM32_SDC_SDIO_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SDC_SDIO_IRQ_PRIORITY 9 +#endif + +/** + * @brief Write timeout in milliseconds. + */ +#if !defined(STM32_SDC_WRITE_TIMEOUT_MS) || defined(__DOXYGEN__) +#define STM32_SDC_WRITE_TIMEOUT_MS 250 +#endif + +/** + * @brief Read timeout in milliseconds. + */ +#if !defined(STM32_SDC_READ_TIMEOUT_MS) || defined(__DOXYGEN__) +#define STM32_SDC_READ_TIMEOUT_MS 25 +#endif + +/** + * @brief Card clock activation delay in milliseconds. + */ +#if !defined(STM32_SDC_CLOCK_ACTIVATION_DELAY) || defined(__DOXYGEN__) +#define STM32_SDC_CLOCK_ACTIVATION_DELAY 10 +#endif + +/** + * @brief Support for unaligned transfers. + * @note Unaligned transfers are much slower. + */ +#if !defined(STM32_SDC_SDIO_UNALIGNED_SUPPORT) || defined(__DOXYGEN__) +#define STM32_SDC_SDIO_UNALIGNED_SUPPORT TRUE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if !STM32_HAS_SDIO +#error "SDIO not present in the selected device" +#endif + +#if !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SDC_SDIO_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SDIO" +#endif + +#if !STM32_DMA_IS_VALID_PRIORITY(STM32_SDC_SDIO_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SDIO" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if !defined(STM32_SDC_SDIO_DMA_STREAM) +#error "SDIO DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if !STM32_DMA_IS_VALID_ID(STM32_SDC_SDIO_DMA_STREAM, STM32_SDC_SDIO_DMA_MSK) +#error "invalid DMA stream associated to SDIO" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/* + * SDIO clock divider. + */ +#if (defined(STM32F4XX) || defined(STM32F2XX)) +#define STM32_SDIO_DIV_HS 0 +#define STM32_SDIO_DIV_LS 120 + +#elif STM32_HCLK > 48000000 +#define STM32_SDIO_DIV_HS 1 +#define STM32_SDIO_DIV_LS 178 +#else + +#define STM32_SDIO_DIV_HS 0 +#define STM32_SDIO_DIV_LS 118 +#endif + +/** + * @brief SDIO data timeouts in SDIO clock cycles. + */ +#if (defined(STM32F4XX) || defined(STM32F2XX)) +#if !STM32_CLOCK48_REQUIRED +#error "SDIO requires STM32_CLOCK48_REQUIRED to be enabled" +#endif + +#if STM32_PLL48CLK != 48000000 +#error "invalid STM32_PLL48CLK clock value" +#endif + +#define STM32_SDC_WRITE_TIMEOUT \ + (((STM32_PLL48CLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ + STM32_SDC_WRITE_TIMEOUT_MS) +#define STM32_SDC_READ_TIMEOUT \ + (((STM32_PLL48CLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ + STM32_SDC_READ_TIMEOUT_MS) + +#else /* !(defined(STM32F4XX) || defined(STM32F2XX)) */ + +#define STM32_SDC_WRITE_TIMEOUT \ + (((STM32_HCLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ + STM32_SDC_WRITE_TIMEOUT_MS) +#define STM32_SDC_READ_TIMEOUT \ + (((STM32_HCLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ + STM32_SDC_READ_TIMEOUT_MS) + +#endif /* !(defined(STM32F4XX) || defined(STM32F2XX)) */ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of card flags. + */ +typedef uint32_t sdcmode_t; + +/** + * @brief SDC Driver condition flags type. + */ +typedef uint32_t sdcflags_t; + +/** + * @brief Type of a structure representing an SDC driver. + */ +typedef struct SDCDriver SDCDriver; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Working area for memory consuming operations. + * @note Buffer must be word aligned and big enough to store 512 bytes. + * @note It is mandatory for detecting MMC cards bigger than 2GB else it + * can be @p NULL. SD cards do NOT need it. + * @note Memory pointed by this buffer is only used by @p sdcConnect(), + * afterward it can be reused for other purposes. + */ + uint8_t *scratchpad; + /** + * @brief Bus width. + */ + sdcbusmode_t bus_width; + /* End of the mandatory fields.*/ +} SDCConfig; + +/** + * @brief @p SDCDriver specific methods. + */ +#define _sdc_driver_methods \ + _mmcsd_block_device_methods + +/** + * @extends MMCSDBlockDeviceVMT + * + * @brief @p SDCDriver virtual methods table. + */ +struct SDCDriverVMT { + _sdc_driver_methods +}; + +/** + * @brief Structure representing an SDC driver. + */ +struct SDCDriver { + /** + * @brief Virtual Methods Table. + */ + const struct SDCDriverVMT *vmt; + _mmcsd_block_device_data + /** + * @brief Current configuration data. + */ + const SDCConfig *config; + /** + * @brief Various flags regarding the mounted card. + */ + sdcmode_t cardmode; + /** + * @brief Errors flags. + */ + sdcflags_t errors; + /** + * @brief Card RCA. + */ + uint32_t rca; + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion IRQ. + */ + thread_reference_t thread; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dma; + /** + * @brief Pointer to the SDIO registers block. + * @note Needed for debugging aid. + */ + SDIO_TypeDef *sdio; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern SDCDriver SDCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sdc_lld_init(void); + void sdc_lld_start(SDCDriver *sdcp); + void sdc_lld_stop(SDCDriver *sdcp); + void sdc_lld_start_clk(SDCDriver *sdcp); + void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); + void sdc_lld_stop_clk(SDCDriver *sdcp); + void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); + void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); + bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t argument); + bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t blocks); + bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t blocks); + bool sdc_lld_sync(SDCDriver *sdcp); + bool sdc_lld_is_card_inserted(SDCDriver *sdcp); + bool sdc_lld_is_write_protected(SDCDriver *sdcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SDC */ + +#endif /* _SDC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.c b/os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.c deleted file mode 100644 index 2519b11b2..000000000 --- a/os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.c +++ /dev/null @@ -1,873 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SDIOv1/sdc_lld.c - * @brief STM32 SDC subsystem low level driver source. - * - * @addtogroup SDC - * @{ - */ - -#include - -#include "hal.h" - -#if HAL_USE_SDC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SDC_SDIO_DMA_STREAM, \ - STM32_SDC_SDIO_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief SDCD1 driver identifier.*/ -SDCDriver SDCD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT -/** - * @brief Buffer for temporary storage during unaligned transfers. - */ -static union { - uint32_t alignment; - uint8_t buf[MMCSD_BLOCK_SIZE]; -} u; -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - - -/** - * @brief SDIO default configuration. - */ -static const SDCConfig sdc_default_cfg = { - NULL, - SDC_MODE_4BIT -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Prepares to handle read transaction. - * @details Designed for read special registers from card. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[out] buf pointer to the read buffer - * @param[in] bytes number of bytes to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -static bool sdc_lld_prepare_read_bytes(SDCDriver *sdcp, - uint8_t *buf, uint32_t bytes) { - osalDbgCheck(bytes < 0x1000000); - - sdcp->sdio->DTIMER = STM32_SDC_READ_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, bytes / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; - sdcp->sdio->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_RXOVERRIE | - SDIO_MASK_DATAENDIE; - sdcp->sdio->DLEN = bytes; - - /* Transaction starts just after DTEN bit setting.*/ - sdcp->sdio->DCTRL = SDIO_DCTRL_DTDIR | - SDIO_DCTRL_DTMODE | /* multibyte data transfer */ - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - - return HAL_SUCCESS; -} - -/** - * @brief Prepares card to handle read transaction. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[in] n number of blocks to read - * @param[in] resp pointer to the response buffer - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -static bool sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, - uint32_t n, uint32_t *resp) { - - /* Driver handles data in 512 bytes blocks (just like HC cards). But if we - have not HC card than we must convert address from blocks to bytes.*/ - if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) - startblk *= MMCSD_BLOCK_SIZE; - - if (n > 1) { - /* Send read multiple blocks command to card.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - else{ - /* Send read single block command.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - - return HAL_SUCCESS; -} - -/** - * @brief Prepares card to handle write transaction. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[in] n number of blocks to write - * @param[in] resp pointer to the response buffer - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -static bool sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk, - uint32_t n, uint32_t *resp) { - - /* Driver handles data in 512 bytes blocks (just like HC cards). But if we - have not HC card than we must convert address from blocks to bytes.*/ - if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) - startblk *= MMCSD_BLOCK_SIZE; - - if (n > 1) { - /* Write multiple blocks command.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - else{ - /* Write single block command.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - - return HAL_SUCCESS; -} - -/** - * @brief Wait end of data transaction and performs finalizations. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] n number of blocks in transaction - * @param[in] resp pointer to the response buffer - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - */ -static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, - uint32_t *resp) { - - /* Note the mask is checked before going to sleep because the interrupt - may have occurred before reaching the critical zone.*/ - osalSysLock(); - if (sdcp->sdio->MASK != 0) - osalThreadSuspendS(&sdcp->thread); - if ((sdcp->sdio->STA & SDIO_STA_DATAEND) == 0) { - osalSysUnlock(); - return HAL_FAILED; - } - -#if (defined(STM32F4XX) || defined(STM32F2XX)) - /* Wait until DMA channel enabled to be sure that all data transferred.*/ - while (sdcp->dma->stream->CR & STM32_DMA_CR_EN) - ; - - /* DMA event flags must be manually cleared.*/ - dmaStreamClearInterrupt(sdcp->dma); - - sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; - sdcp->sdio->DCTRL = 0; - osalSysUnlock(); - - /* Wait until interrupt flags to be cleared.*/ - /*while (((DMA2->LISR) >> (sdcp->dma->ishift)) & STM32_DMA_ISR_TCIF) - dmaStreamClearInterrupt(sdcp->dma);*/ -#else - /* Waits for transfer completion at DMA level, then the stream is - disabled and cleared.*/ - dmaWaitCompletion(sdcp->dma); - - sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; - sdcp->sdio->DCTRL = 0; - osalSysUnlock(); -#endif - - /* Finalize transaction.*/ - if (n > 1) - return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); - - return HAL_SUCCESS; -} - -/** - * @brief Gets SDC errors. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] sta value of the STA register - * - * @notapi - */ -static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) { - uint32_t errors = SDC_NO_ERROR; - - if (sta & SDIO_STA_CCRCFAIL) - errors |= SDC_CMD_CRC_ERROR; - if (sta & SDIO_STA_DCRCFAIL) - errors |= SDC_DATA_CRC_ERROR; - if (sta & SDIO_STA_CTIMEOUT) - errors |= SDC_COMMAND_TIMEOUT; - if (sta & SDIO_STA_DTIMEOUT) - errors |= SDC_DATA_TIMEOUT; - if (sta & SDIO_STA_TXUNDERR) - errors |= SDC_TX_UNDERRUN; - if (sta & SDIO_STA_RXOVERR) - errors |= SDC_RX_OVERRUN; - if (sta & SDIO_STA_STBITERR) - errors |= SDC_STARTBIT_ERROR; - - sdcp->errors |= errors; -} - -/** - * @brief Performs clean transaction stopping in case of errors. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] n number of blocks in transaction - * @param[in] resp pointer to the response buffer - * - * @notapi - */ -static void sdc_lld_error_cleanup(SDCDriver *sdcp, - uint32_t n, - uint32_t *resp) { - uint32_t sta = sdcp->sdio->STA; - - dmaStreamClearInterrupt(sdcp->dma); - dmaStreamDisable(sdcp->dma); - sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; - sdcp->sdio->MASK = 0; - sdcp->sdio->DCTRL = 0; - sdc_lld_collect_errors(sdcp, sta); - if (n > 1) - sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if !defined(STM32_SDIO_HANDLER) -#error "STM32_SDIO_HANDLER not defined" -#endif -/** - * @brief SDIO IRQ handler. - * @details It just wakes transaction thread. All error handling performs in - * that thread. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_SDIO_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - osalSysLockFromISR(); - - /* Disables the source but the status flags are not reset because the - read/write functions needs to check them.*/ - SDIO->MASK = 0; - - osalThreadResumeI(&SDCD1.thread, MSG_OK); - - osalSysUnlockFromISR(); - - OSAL_IRQ_EPILOGUE(); -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SDC driver initialization. - * - * @notapi - */ -void sdc_lld_init(void) { - - sdcObjectInit(&SDCD1); - SDCD1.thread = NULL; - SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDIO_DMA_STREAM); - SDCD1.sdio = SDIO; -} - -/** - * @brief Configures and activates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start(SDCDriver *sdcp) { - - /* Checking configuration, using a default if NULL has been passed.*/ - if (sdcp->config == NULL) { - sdcp->config = &sdc_default_cfg; - } - - sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SDC_SDIO_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_WORD | - STM32_DMA_CR_MSIZE_WORD | - STM32_DMA_CR_MINC; - -#if (defined(STM32F4XX) || defined(STM32F2XX)) - sdcp->dmamode |= STM32_DMA_CR_PFCTRL | - STM32_DMA_CR_PBURST_INCR4 | - STM32_DMA_CR_MBURST_INCR4; -#endif - - if (sdcp->state == BLK_STOP) { - /* Note, the DMA must be enabled before the IRQs.*/ - bool b; - b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDIO_IRQ_PRIORITY, NULL, NULL); - osalDbgAssert(!b, "stream already allocated"); - dmaStreamSetPeripheral(sdcp->dma, &sdcp->sdio->FIFO); -#if (defined(STM32F4XX) || defined(STM32F2XX)) - dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); -#endif - nvicEnableVector(STM32_SDIO_NUMBER, STM32_SDC_SDIO_IRQ_PRIORITY); - rccEnableSDIO(FALSE); - } - - /* Configuration, card clock is initially stopped.*/ - sdcp->sdio->POWER = 0; - sdcp->sdio->CLKCR = 0; - sdcp->sdio->DCTRL = 0; - sdcp->sdio->DTIMER = 0; -} - -/** - * @brief Deactivates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop(SDCDriver *sdcp) { - - if (sdcp->state != BLK_STOP) { - - /* SDIO deactivation.*/ - sdcp->sdio->POWER = 0; - sdcp->sdio->CLKCR = 0; - sdcp->sdio->DCTRL = 0; - sdcp->sdio->DTIMER = 0; - - /* Clock deactivation.*/ - nvicDisableVector(STM32_SDIO_NUMBER); - dmaStreamRelease(sdcp->dma); - rccDisableSDIO(FALSE); - } -} - -/** - * @brief Starts the SDIO clock and sets it to init mode (400kHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start_clk(SDCDriver *sdcp) { - - /* Initial clock setting: 400kHz, 1bit mode.*/ - sdcp->sdio->CLKCR = STM32_SDIO_DIV_LS; - sdcp->sdio->POWER |= SDIO_POWER_PWRCTRL_0 | SDIO_POWER_PWRCTRL_1; - sdcp->sdio->CLKCR |= SDIO_CLKCR_CLKEN; - - /* Clock activation delay.*/ - osalThreadSleep(OSAL_MS2ST(STM32_SDC_CLOCK_ACTIVATION_DELAY)); -} - -/** - * @brief Sets the SDIO clock to data mode (25MHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] clk the clock mode - * - * @notapi - */ -void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { -#if 0 - if (SDC_CLK_50MHz == clk) { - sdcp->sdio->CLKCR = (sdcp->sdio->CLKCR & 0xFFFFFF00U) | STM32_SDIO_DIV_HS - | SDIO_CLKCR_BYPASS; - } - else - sdcp->sdio->CLKCR = (sdcp->sdio->CLKCR & 0xFFFFFF00U) | STM32_SDIO_DIV_HS; -#else - (void)clk; - - sdcp->sdio->CLKCR = (sdcp->sdio->CLKCR & 0xFFFFFF00U) | STM32_SDIO_DIV_HS; -#endif -} - -/** - * @brief Stops the SDIO clock. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop_clk(SDCDriver *sdcp) { - - sdcp->sdio->CLKCR = 0; - sdcp->sdio->POWER = 0; -} - -/** - * @brief Switches the bus to 4 bits mode. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] mode bus mode - * - * @notapi - */ -void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { - uint32_t clk = sdcp->sdio->CLKCR & ~SDIO_CLKCR_WIDBUS; - - switch (mode) { - case SDC_MODE_1BIT: - sdcp->sdio->CLKCR = clk; - break; - case SDC_MODE_4BIT: - sdcp->sdio->CLKCR = clk | SDIO_CLKCR_WIDBUS_0; - break; - case SDC_MODE_8BIT: - sdcp->sdio->CLKCR = clk | SDIO_CLKCR_WIDBUS_1; - break; - } -} - -/** - * @brief Sends an SDIO command with no response expected. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * - * @notapi - */ -void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { - - sdcp->sdio->ARG = arg; - sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_CPSMEN; - while ((sdcp->sdio->STA & SDIO_STA_CMDSENT) == 0) - ; - sdcp->sdio->ICR = SDIO_ICR_CMDSENTC; -} - -/** - * @brief Sends an SDIO command with a short response expected. - * @note The CRC is not verified. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (one word) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - sdcp->sdio->ARG = arg; - sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; - while (((sta = sdcp->sdio->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - sdcp->sdio->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL); - if ((sta & (SDIO_STA_CTIMEOUT)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return HAL_FAILED; - } - *resp = sdcp->sdio->RESP1; - return HAL_SUCCESS; -} - -/** - * @brief Sends an SDIO command with a short response expected and CRC. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (one word) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - sdcp->sdio->ARG = arg; - sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_CPSMEN; - while (((sta = sdcp->sdio->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - sdcp->sdio->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL); - if ((sta & (SDIO_STA_CTIMEOUT | SDIO_STA_CCRCFAIL)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return HAL_FAILED; - } - *resp = sdcp->sdio->RESP1; - return HAL_SUCCESS; -} - -/** - * @brief Sends an SDIO command with a long response expected and CRC. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (four words) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - sdcp->sdio->ARG = arg; - sdcp->sdio->CMD = (uint32_t)cmd | SDIO_CMD_WAITRESP_0 | SDIO_CMD_WAITRESP_1 | - SDIO_CMD_CPSMEN; - while (((sta = sdcp->sdio->STA) & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL)) == 0) - ; - sdcp->sdio->ICR = sta & (SDIO_STA_CMDREND | SDIO_STA_CTIMEOUT | - SDIO_STA_CCRCFAIL); - if ((sta & (STM32_SDIO_STA_ERROR_MASK)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return HAL_FAILED; - } - /* Save bytes in reverse order because MSB in response comes first.*/ - *resp++ = sdcp->sdio->RESP4; - *resp++ = sdcp->sdio->RESP3; - *resp++ = sdcp->sdio->RESP2; - *resp = sdcp->sdio->RESP1; - return HAL_SUCCESS; -} - -/** - * @brief Reads special registers using data bus. - * @details Needs only during card detection procedure. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[out] buf pointer to the read buffer - * @param[in] bytes number of bytes to read - * @param[in] cmd card command - * @param[in] arg argument for command - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, - uint8_t cmd, uint32_t arg) { - uint32_t resp[1]; - - if(sdc_lld_prepare_read_bytes(sdcp, buf, bytes)) - goto error; - - if (sdc_lld_send_cmd_short_crc(sdcp, cmd, arg, resp) - || MMCSD_R1_ERROR(resp[0])) - goto error; - - if (sdc_lld_wait_transaction_end(sdcp, 1, resp)) - goto error; - - return HAL_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, 1, resp); - return HAL_FAILED; -} - -/** - * @brief Reads one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[out] buf pointer to the read buffer - * @param[in] blocks number of blocks to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t blocks) { - uint32_t resp[1]; - - osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); - - sdcp->sdio->DTIMER = STM32_SDC_READ_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; - sdcp->sdio->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_RXOVERRIE | - SDIO_MASK_DATAENDIE; - sdcp->sdio->DLEN = blocks * MMCSD_BLOCK_SIZE; - - /* Transaction starts just after DTEN bit setting.*/ - sdcp->sdio->DCTRL = SDIO_DCTRL_DTDIR | - SDIO_DCTRL_DBLOCKSIZE_3 | - SDIO_DCTRL_DBLOCKSIZE_0 | - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - - if (sdc_lld_prepare_read(sdcp, startblk, blocks, resp) == TRUE) - goto error; - - if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) - goto error; - - return HAL_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, blocks, resp); - return HAL_FAILED; -} - -/** - * @brief Writes one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to write - * @param[out] buf pointer to the write buffer - * @param[in] n number of blocks to write - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t blocks) { - uint32_t resp[1]; - - osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); - - sdcp->sdio->DTIMER = STM32_SDC_WRITE_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for writing.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - sdcp->sdio->ICR = STM32_SDIO_ICR_ALL_FLAGS; - sdcp->sdio->MASK = SDIO_MASK_DCRCFAILIE | - SDIO_MASK_DTIMEOUTIE | - SDIO_MASK_STBITERRIE | - SDIO_MASK_TXUNDERRIE | - SDIO_MASK_DATAENDIE; - sdcp->sdio->DLEN = blocks * MMCSD_BLOCK_SIZE; - - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_write(sdcp, startblk, blocks, resp) == TRUE) - goto error; - - /* Transaction starts just after DTEN bit setting.*/ - sdcp->sdio->DCTRL = SDIO_DCTRL_DBLOCKSIZE_3 | - SDIO_DCTRL_DBLOCKSIZE_0 | - SDIO_DCTRL_DMAEN | - SDIO_DCTRL_DTEN; - - if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) - goto error; - - return HAL_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, blocks, resp); - return HAL_FAILED; -} - -/** - * @brief Reads one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[out] buf pointer to the read buffer - * @param[in] blocks number of blocks to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t blocks) { - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < blocks; i++) { - if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) - return HAL_FAILED; - memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - startblk++; - } - return HAL_SUCCESS; - } -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_read_aligned(sdcp, startblk, buf, blocks); -} - -/** - * @brief Writes one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to write - * @param[out] buf pointer to the write buffer - * @param[in] blocks number of blocks to write - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t blocks) { - -#if STM32_SDC_SDIO_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < blocks; i++) { - memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) - return HAL_FAILED; - startblk++; - } - return HAL_SUCCESS; - } -#endif /* STM32_SDC_SDIO_UNALIGNED_SUPPORT */ - return sdc_lld_write_aligned(sdcp, startblk, buf, blocks); -} - -/** - * @brief Waits for card idle condition. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @return The operation status. - * @retval HAL_SUCCESS the operation succeeded. - * @retval HAL_FAILED the operation failed. - * - * @api - */ -bool sdc_lld_sync(SDCDriver *sdcp) { - - /* TODO: Implement.*/ - (void)sdcp; - return HAL_SUCCESS; -} - -#endif /* HAL_USE_SDC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.h b/os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.h deleted file mode 100644 index baf1e9c61..000000000 --- a/os/hal/ports/STM32/LLD/SDIOv1/sdc_lld.h +++ /dev/null @@ -1,330 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SDIOv1/sdc_lld.h - * @brief STM32 SDC subsystem low level driver header. - * - * @addtogroup SDC - * @{ - */ - -#ifndef _SDC_LLD_H_ -#define _SDC_LLD_H_ - -#if HAL_USE_SDC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Value to clear all interrupts flag at once. - */ -#define STM32_SDIO_ICR_ALL_FLAGS (SDIO_ICR_CCRCFAILC | SDIO_ICR_DCRCFAILC | \ - SDIO_ICR_CTIMEOUTC | SDIO_ICR_DTIMEOUTC | \ - SDIO_ICR_TXUNDERRC | SDIO_ICR_RXOVERRC | \ - SDIO_ICR_CMDRENDC | SDIO_ICR_CMDSENTC | \ - SDIO_ICR_DATAENDC | SDIO_ICR_STBITERRC | \ - SDIO_ICR_DBCKENDC | SDIO_ICR_SDIOITC | \ - SDIO_ICR_CEATAENDC) - -/** - * @brief Mask of error flags in STA register. - */ -#define STM32_SDIO_STA_ERROR_MASK (SDIO_STA_CCRCFAIL | SDIO_STA_DCRCFAIL | \ - SDIO_STA_CTIMEOUT | SDIO_STA_DTIMEOUT | \ - SDIO_STA_TXUNDERR | SDIO_STA_RXOVERR) - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief SDIO DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_SDC_SDIO_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SDC_SDIO_DMA_PRIORITY 3 -#endif - -/** - * @brief SDIO interrupt priority level setting. - */ -#if !defined(STM32_SDC_SDIO_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SDC_SDIO_IRQ_PRIORITY 9 -#endif - -/** - * @brief Write timeout in milliseconds. - */ -#if !defined(STM32_SDC_WRITE_TIMEOUT_MS) || defined(__DOXYGEN__) -#define STM32_SDC_WRITE_TIMEOUT_MS 250 -#endif - -/** - * @brief Read timeout in milliseconds. - */ -#if !defined(STM32_SDC_READ_TIMEOUT_MS) || defined(__DOXYGEN__) -#define STM32_SDC_READ_TIMEOUT_MS 25 -#endif - -/** - * @brief Card clock activation delay in milliseconds. - */ -#if !defined(STM32_SDC_CLOCK_ACTIVATION_DELAY) || defined(__DOXYGEN__) -#define STM32_SDC_CLOCK_ACTIVATION_DELAY 10 -#endif - -/** - * @brief Support for unaligned transfers. - * @note Unaligned transfers are much slower. - */ -#if !defined(STM32_SDC_SDIO_UNALIGNED_SUPPORT) || defined(__DOXYGEN__) -#define STM32_SDC_SDIO_UNALIGNED_SUPPORT TRUE -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if !STM32_HAS_SDIO -#error "SDIO not present in the selected device" -#endif - -#if !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SDC_SDIO_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SDIO" -#endif - -#if !STM32_DMA_IS_VALID_PRIORITY(STM32_SDC_SDIO_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SDIO" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if !defined(STM32_SDC_SDIO_DMA_STREAM) -#error "SDIO DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if !STM32_DMA_IS_VALID_ID(STM32_SDC_SDIO_DMA_STREAM, STM32_SDC_SDIO_DMA_MSK) -#error "invalid DMA stream associated to SDIO" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/* - * SDIO clock divider. - */ -#if (defined(STM32F4XX) || defined(STM32F2XX)) -#define STM32_SDIO_DIV_HS 0 -#define STM32_SDIO_DIV_LS 120 - -#elif STM32_HCLK > 48000000 -#define STM32_SDIO_DIV_HS 1 -#define STM32_SDIO_DIV_LS 178 -#else - -#define STM32_SDIO_DIV_HS 0 -#define STM32_SDIO_DIV_LS 118 -#endif - -/** - * @brief SDIO data timeouts in SDIO clock cycles. - */ -#if (defined(STM32F4XX) || defined(STM32F2XX)) -#if !STM32_CLOCK48_REQUIRED -#error "SDIO requires STM32_CLOCK48_REQUIRED to be enabled" -#endif - -#if STM32_PLL48CLK != 48000000 -#error "invalid STM32_PLL48CLK clock value" -#endif - -#define STM32_SDC_WRITE_TIMEOUT \ - (((STM32_PLL48CLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ - STM32_SDC_WRITE_TIMEOUT_MS) -#define STM32_SDC_READ_TIMEOUT \ - (((STM32_PLL48CLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ - STM32_SDC_READ_TIMEOUT_MS) - -#else /* !(defined(STM32F4XX) || defined(STM32F2XX)) */ - -#define STM32_SDC_WRITE_TIMEOUT \ - (((STM32_HCLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ - STM32_SDC_WRITE_TIMEOUT_MS) -#define STM32_SDC_READ_TIMEOUT \ - (((STM32_HCLK / (STM32_SDIO_DIV_HS + 2)) / 1000) * \ - STM32_SDC_READ_TIMEOUT_MS) - -#endif /* !(defined(STM32F4XX) || defined(STM32F2XX)) */ - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of card flags. - */ -typedef uint32_t sdcmode_t; - -/** - * @brief SDC Driver condition flags type. - */ -typedef uint32_t sdcflags_t; - -/** - * @brief Type of a structure representing an SDC driver. - */ -typedef struct SDCDriver SDCDriver; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Working area for memory consuming operations. - * @note Buffer must be word aligned and big enough to store 512 bytes. - * @note It is mandatory for detecting MMC cards bigger than 2GB else it - * can be @p NULL. SD cards do NOT need it. - * @note Memory pointed by this buffer is only used by @p sdcConnect(), - * afterward it can be reused for other purposes. - */ - uint8_t *scratchpad; - /** - * @brief Bus width. - */ - sdcbusmode_t bus_width; - /* End of the mandatory fields.*/ -} SDCConfig; - -/** - * @brief @p SDCDriver specific methods. - */ -#define _sdc_driver_methods \ - _mmcsd_block_device_methods - -/** - * @extends MMCSDBlockDeviceVMT - * - * @brief @p SDCDriver virtual methods table. - */ -struct SDCDriverVMT { - _sdc_driver_methods -}; - -/** - * @brief Structure representing an SDC driver. - */ -struct SDCDriver { - /** - * @brief Virtual Methods Table. - */ - const struct SDCDriverVMT *vmt; - _mmcsd_block_device_data - /** - * @brief Current configuration data. - */ - const SDCConfig *config; - /** - * @brief Various flags regarding the mounted card. - */ - sdcmode_t cardmode; - /** - * @brief Errors flags. - */ - sdcflags_t errors; - /** - * @brief Card RCA. - */ - uint32_t rca; - /* End of the mandatory fields.*/ - /** - * @brief Thread waiting for I/O completion IRQ. - */ - thread_reference_t thread; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dma; - /** - * @brief Pointer to the SDIO registers block. - * @note Needed for debugging aid. - */ - SDIO_TypeDef *sdio; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern SDCDriver SDCD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void sdc_lld_init(void); - void sdc_lld_start(SDCDriver *sdcp); - void sdc_lld_stop(SDCDriver *sdcp); - void sdc_lld_start_clk(SDCDriver *sdcp); - void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); - void sdc_lld_stop_clk(SDCDriver *sdcp); - void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); - void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); - bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, - uint8_t cmd, uint32_t argument); - bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t blocks); - bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t blocks); - bool sdc_lld_sync(SDCDriver *sdcp); - bool sdc_lld_is_card_inserted(SDCDriver *sdcp); - bool sdc_lld_is_write_protected(SDCDriver *sdcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SDC */ - -#endif /* _SDC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.c b/os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.c new file mode 100644 index 000000000..2e1d9b051 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.c @@ -0,0 +1,885 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SDMMCv1/sdc_lld.c + * @brief STM32 SDC subsystem low level driver source. + * + * @addtogroup SDC + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_SDC || defined(__DOXYGEN__) + +#if !defined(STM32_SDMMCCLK) +#error "STM32_SDMMCCLK not defined" +#endif + +#if STM32_SDMMCCLK > 48000000 +#error "STM32_SDMMCCLK exceeding 48MHz" +#endif + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define SDMMC_ICR_ALL_FLAGS \ + (SDMMC_ICR_CCRCFAILC | SDMMC_ICR_DCRCFAILC | \ + SDMMC_ICR_CTIMEOUTC | SDMMC_ICR_DTIMEOUTC | \ + SDMMC_ICR_TXUNDERRC | SDMMC_ICR_RXOVERRC | \ + SDMMC_ICR_CMDRENDC | SDMMC_ICR_CMDSENTC | \ + SDMMC_ICR_DATAENDC | SDMMC_ICR_DBCKENDC | \ + SDMMC_ICR_SDIOITC) + +#define SDMMC_STA_ERROR_MASK \ + (SDMMC_STA_CCRCFAIL | SDMMC_STA_DCRCFAIL | \ + SDMMC_STA_CTIMEOUT | SDMMC_STA_DTIMEOUT | \ + SDMMC_STA_TXUNDERR | SDMMC_STA_RXOVERR) + +#define SDMMC_CLKDIV_HS (2 - 2) +#define SDMMC_CLKDIV_LS (120 - 2) + +#define SDMMC_WRITE_TIMEOUT \ + (((STM32_SDMMCCLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \ + STM32_SDC_SDMMC_WRITE_TIMEOUT) +#define SDMMC_READ_TIMEOUT \ + (((STM32_SDMMCCLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \ + STM32_SDC_SDMMC_READ_TIMEOUT) + +#define DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SDC_SDMMC1_DMA_STREAM, \ + STM32_SDC_SDMMC1_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief SDCD1 driver identifier.*/ +SDCDriver SDCD1; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT +/** + * @brief Buffer for temporary storage during unaligned transfers. + */ +static union { + uint32_t alignment; + uint8_t buf[MMCSD_BLOCK_SIZE]; +} u; +#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */ + + +/** + * @brief SDIO default configuration. + */ +static const SDCConfig sdc_default_cfg = { + NULL, + SDC_MODE_4BIT +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Prepares to handle read transaction. + * @details Designed for read special registers from card. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] buf pointer to the read buffer + * @param[in] bytes number of bytes to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_read_bytes(SDCDriver *sdcp, + uint8_t *buf, uint32_t bytes) { + osalDbgCheck(bytes < 0x1000000); + + sdcp->sdmmc->DTIMER = SDMMC_READ_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for reading.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, bytes / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; + sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE | + SDMMC_MASK_DTIMEOUTIE | + SDMMC_MASK_RXOVERRIE | + SDMMC_MASK_DATAENDIE; + sdcp->sdmmc->DLEN = bytes; + + /* Transaction starts just after DTEN bit setting.*/ + sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DTDIR | + SDMMC_DCTRL_DTMODE | /* multibyte data transfer */ + SDMMC_DCTRL_DMAEN | + SDMMC_DCTRL_DTEN; + + return HAL_SUCCESS; +} + +/** + * @brief Prepares card to handle read transaction. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[in] n number of blocks to read + * @param[in] resp pointer to the response buffer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, + uint32_t n, uint32_t *resp) { + + /* Driver handles data in 512 bytes blocks (just like HC cards). But if we + have not HC card than we must convert address from blocks to bytes.*/ + if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) + startblk *= MMCSD_BLOCK_SIZE; + + if (n > 1) { + /* Send read multiple blocks command to card.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + else{ + /* Send read single block command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + + return HAL_SUCCESS; +} + +/** + * @brief Prepares card to handle write transaction. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[in] n number of blocks to write + * @param[in] resp pointer to the response buffer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +static bool sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk, + uint32_t n, uint32_t *resp) { + + /* Driver handles data in 512 bytes blocks (just like HC cards). But if we + have not HC card than we must convert address from blocks to bytes.*/ + if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) + startblk *= MMCSD_BLOCK_SIZE; + + if (n > 1) { + /* Write multiple blocks command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + else{ + /* Write single block command.*/ + if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_BLOCK, + startblk, resp) || MMCSD_R1_ERROR(resp[0])) + return HAL_FAILED; + } + + return HAL_SUCCESS; +} + +/** + * @brief Wait end of data transaction and performs finalizations. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] n number of blocks in transaction + * @param[in] resp pointer to the response buffer + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + */ +static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, + uint32_t *resp) { + + /* Note the mask is checked before going to sleep because the interrupt + may have occurred before reaching the critical zone.*/ + osalSysLock(); + if (sdcp->sdmmc->MASK != 0) + osalThreadSuspendS(&sdcp->thread); + if ((sdcp->sdmmc->STA & SDMMC_STA_DATAEND) == 0) { + osalSysUnlock(); + return HAL_FAILED; + } + + /* Waits for transfer completion at DMA level, then the stream is + disabled and cleared.*/ + dmaWaitCompletion(sdcp->dma); + + sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; + sdcp->sdmmc->DCTRL = 0; + osalSysUnlock(); + + /* Finalize transaction.*/ + if (n > 1) + return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); + + return HAL_SUCCESS; +} + +/** + * @brief Gets SDC errors. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] sta value of the STA register + * + * @notapi + */ +static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) { + uint32_t errors = SDC_NO_ERROR; + + if (sta & SDMMC_STA_CCRCFAIL) + errors |= SDC_CMD_CRC_ERROR; + if (sta & SDMMC_STA_DCRCFAIL) + errors |= SDC_DATA_CRC_ERROR; + if (sta & SDMMC_STA_CTIMEOUT) + errors |= SDC_COMMAND_TIMEOUT; + if (sta & SDMMC_STA_DTIMEOUT) + errors |= SDC_DATA_TIMEOUT; + if (sta & SDMMC_STA_TXUNDERR) + errors |= SDC_TX_UNDERRUN; + if (sta & SDMMC_STA_RXOVERR) + errors |= SDC_RX_OVERRUN; +/* if (sta & SDMMC_STA_STBITERR) + errors |= SDC_STARTBIT_ERROR;*/ + + sdcp->errors |= errors; +} + +/** + * @brief Performs clean transaction stopping in case of errors. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] n number of blocks in transaction + * @param[in] resp pointer to the response buffer + * + * @notapi + */ +static void sdc_lld_error_cleanup(SDCDriver *sdcp, + uint32_t n, + uint32_t *resp) { + uint32_t sta = sdcp->sdmmc->STA; + + dmaStreamDisable(sdcp->dma); + sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; + sdcp->sdmmc->MASK = 0; + sdcp->sdmmc->DCTRL = 0; + sdc_lld_collect_errors(sdcp, sta); + + if (n > 1) + sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if !defined(STM32_SDMMC1_HANDLER) +#error "STM32_SDMMC1_HANDLER not defined" +#endif + +/** + * @brief SDIO IRQ handler. + * @details It just wakes transaction thread. All error handling performs in + * that thread. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_SDMMC1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + osalSysLockFromISR(); + + /* Disables the source but the status flags are not reset because the + read/write functions needs to check them.*/ + SDMMC1->MASK = 0; + + osalThreadResumeI(&SDCD1.thread, MSG_OK); + + osalSysUnlockFromISR(); + + OSAL_IRQ_EPILOGUE(); +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SDC driver initialization. + * + * @notapi + */ +void sdc_lld_init(void) { + + sdcObjectInit(&SDCD1); + SDCD1.thread = NULL; + SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDMMC1_DMA_STREAM); + SDCD1.sdmmc = SDMMC1; +} + +/** + * @brief Configures and activates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start(SDCDriver *sdcp) { + + /* Checking configuration, using a default if NULL has been passed.*/ + if (sdcp->config == NULL) { + sdcp->config = &sdc_default_cfg; + } + + sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SDC_SDMMC1_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_WORD | + STM32_DMA_CR_MSIZE_WORD | + STM32_DMA_CR_MINC; + +#if STM32_DMA_ADVANCED + sdcp->dmamode |= STM32_DMA_CR_PFCTRL | + STM32_DMA_CR_PBURST_INCR4 | + STM32_DMA_CR_MBURST_INCR4; +#endif + + if (sdcp->state == BLK_STOP) { + /* Note, the DMA must be enabled before the IRQs.*/ + bool b; + b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDMMC1_IRQ_PRIORITY, NULL, NULL); + osalDbgAssert(!b, "stream already allocated"); + dmaStreamSetPeripheral(sdcp->dma, &sdcp->sdmmc->FIFO); +#if STM32_DMA_ADVANCED + dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); +#endif + nvicEnableVector(STM32_SDMMC1_NUMBER, STM32_SDC_SDMMC1_IRQ_PRIORITY); + rccEnableSDMMC1(FALSE); + } + + /* Configuration, card clock is initially stopped.*/ + sdcp->sdmmc->POWER = 0; + sdcp->sdmmc->CLKCR = 0; + sdcp->sdmmc->DCTRL = 0; + sdcp->sdmmc->DTIMER = 0; +} + +/** + * @brief Deactivates the SDC peripheral. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop(SDCDriver *sdcp) { + + if (sdcp->state != BLK_STOP) { + + /* SDIO deactivation.*/ + sdcp->sdmmc->POWER = 0; + sdcp->sdmmc->CLKCR = 0; + sdcp->sdmmc->DCTRL = 0; + sdcp->sdmmc->DTIMER = 0; + + /* Clock deactivation.*/ + nvicDisableVector(STM32_SDMMC1_NUMBER); + dmaStreamRelease(sdcp->dma); + rccDisableSDMMC1(FALSE); + } +} + +/** + * @brief Starts the SDIO clock and sets it to init mode (400kHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_start_clk(SDCDriver *sdcp) { + + /* Initial clock setting: 400kHz, 1bit mode.*/ + sdcp->sdmmc->CLKCR = SDMMC_CLKDIV_LS; + sdcp->sdmmc->POWER |= SDMMC_POWER_PWRCTRL_0 | SDMMC_POWER_PWRCTRL_1; + sdcp->sdmmc->CLKCR |= SDMMC_CLKCR_CLKEN; + + /* Clock activation delay.*/ + osalThreadSleep(OSAL_MS2ST(STM32_SDC_SDMMC_CLOCK_DELAY)); +} + +/** + * @brief Sets the SDIO clock to data mode (25MHz or less). + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] clk the clock mode + * + * @notapi + */ +void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { +#if 0 + if (SDC_CLK_50MHz == clk) { + sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | + SDMMC_CLKDIV_HS | SDMMC_CLKCR_BYPASS; + } + else + sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS; +#else + (void)clk; + + sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS; +#endif +} + +/** + * @brief Stops the SDIO clock. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @notapi + */ +void sdc_lld_stop_clk(SDCDriver *sdcp) { + + sdcp->sdmmc->CLKCR = 0; + sdcp->sdmmc->POWER = 0; +} + +/** + * @brief Switches the bus to 4 bits mode. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] mode bus mode + * + * @notapi + */ +void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { + uint32_t clk = sdcp->sdmmc->CLKCR & ~SDMMC_CLKCR_WIDBUS; + + switch (mode) { + case SDC_MODE_1BIT: + sdcp->sdmmc->CLKCR = clk; + break; + case SDC_MODE_4BIT: + sdcp->sdmmc->CLKCR = clk | SDMMC_CLKCR_WIDBUS_0; + break; + case SDC_MODE_8BIT: + sdcp->sdmmc->CLKCR = clk | SDMMC_CLKCR_WIDBUS_1; + break; + } +} + +/** + * @brief Sends an SDIO command with no response expected. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * + * @notapi + */ +void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { + + sdcp->sdmmc->ARG = arg; + sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_CPSMEN; + while ((sdcp->sdmmc->STA & SDMMC_STA_CMDSENT) == 0) + ; + sdcp->sdmmc->ICR = SDMMC_ICR_CMDSENTC; +} + +/** + * @brief Sends an SDIO command with a short response expected. + * @note The CRC is not verified. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (one word) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + sdcp->sdmmc->ARG = arg; + sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN; + while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | + SDMMC_STA_CCRCFAIL)) == 0) + ; + sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | + SDMMC_STA_CCRCFAIL); + if ((sta & (SDMMC_STA_CTIMEOUT)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return HAL_FAILED; + } + *resp = sdcp->sdmmc->RESP1; + return HAL_SUCCESS; +} + +/** + * @brief Sends an SDIO command with a short response expected and CRC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (one word) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + sdcp->sdmmc->ARG = arg; + sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN; + while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | + SDMMC_STA_CCRCFAIL)) == 0) + ; + sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | SDMMC_STA_CCRCFAIL); + if ((sta & (SDMMC_STA_CTIMEOUT | SDMMC_STA_CCRCFAIL)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return HAL_FAILED; + } + *resp = sdcp->sdmmc->RESP1; + return HAL_SUCCESS; +} + +/** + * @brief Sends an SDIO command with a long response expected and CRC. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] cmd card command + * @param[in] arg command argument + * @param[out] resp pointer to the response buffer (four words) + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp) { + uint32_t sta; + + (void)sdcp; + + sdcp->sdmmc->ARG = arg; + sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_WAITRESP_1 | + SDMMC_CMD_CPSMEN; + while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | + SDMMC_STA_CCRCFAIL)) == 0) + ; + sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | + SDMMC_STA_CCRCFAIL); + if ((sta & (SDMMC_STA_ERROR_MASK)) != 0) { + sdc_lld_collect_errors(sdcp, sta); + return HAL_FAILED; + } + /* Save bytes in reverse order because MSB in response comes first.*/ + *resp++ = sdcp->sdmmc->RESP4; + *resp++ = sdcp->sdmmc->RESP3; + *resp++ = sdcp->sdmmc->RESP2; + *resp = sdcp->sdmmc->RESP1; + return HAL_SUCCESS; +} + +/** + * @brief Reads special registers using data bus. + * @details Needs only during card detection procedure. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[out] buf pointer to the read buffer + * @param[in] bytes number of bytes to read + * @param[in] cmd card command + * @param[in] arg argument for command + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t arg) { + uint32_t resp[1]; + + if(sdc_lld_prepare_read_bytes(sdcp, buf, bytes)) + goto error; + + if (sdc_lld_send_cmd_short_crc(sdcp, cmd, arg, resp) + || MMCSD_R1_ERROR(resp[0])) + goto error; + + if (sdc_lld_wait_transaction_end(sdcp, 1, resp)) + goto error; + + return HAL_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, 1, resp); + return HAL_FAILED; +} + +/** + * @brief Reads one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[out] buf pointer to the read buffer + * @param[in] blocks number of blocks to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t blocks) { + uint32_t resp[1]; + + osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); + + sdcp->sdmmc->DTIMER = SDMMC_READ_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for reading.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; + sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE | + SDMMC_MASK_DTIMEOUTIE | + SDMMC_MASK_RXOVERRIE | + SDMMC_MASK_DATAENDIE; + sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE; + + /* Transaction starts just after DTEN bit setting.*/ + sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DTDIR | + SDMMC_DCTRL_DBLOCKSIZE_3 | + SDMMC_DCTRL_DBLOCKSIZE_0 | + SDMMC_DCTRL_DMAEN | + SDMMC_DCTRL_DTEN; + + if (sdc_lld_prepare_read(sdcp, startblk, blocks, resp) == TRUE) + goto error; + + if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) + goto error; + + return HAL_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, blocks, resp); + return HAL_FAILED; +} + +/** + * @brief Writes one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to write + * @param[out] buf pointer to the write buffer + * @param[in] n number of blocks to write + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t blocks) { + uint32_t resp[1]; + + osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); + + sdcp->sdmmc->DTIMER = SDMMC_WRITE_TIMEOUT; + + /* Checks for errors and waits for the card to be ready for writing.*/ + if (_sdc_wait_for_transfer_state(sdcp)) + return HAL_FAILED; + + /* Prepares the DMA channel for writing.*/ + dmaStreamSetMemory0(sdcp->dma, buf); + dmaStreamSetTransactionSize(sdcp->dma, + (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); + dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); + dmaStreamEnable(sdcp->dma); + + /* Setting up data transfer.*/ + sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; + sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE | + SDMMC_MASK_DTIMEOUTIE | + SDMMC_MASK_TXUNDERRIE | + SDMMC_MASK_DATAENDIE; + sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE; + + /* Talk to card what we want from it.*/ + if (sdc_lld_prepare_write(sdcp, startblk, blocks, resp) == TRUE) + goto error; + + /* Transaction starts just after DTEN bit setting.*/ + sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DBLOCKSIZE_3 | + SDMMC_DCTRL_DBLOCKSIZE_0 | + SDMMC_DCTRL_DMAEN | + SDMMC_DCTRL_DTEN; + + if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) + goto error; + + return HAL_SUCCESS; + +error: + sdc_lld_error_cleanup(sdcp, blocks, resp); + return HAL_FAILED; +} + +/** + * @brief Reads one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to read + * @param[out] buf pointer to the read buffer + * @param[in] blocks number of blocks to read + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t blocks) { + +#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT + if (((unsigned)buf & 3) != 0) { + uint32_t i; + for (i = 0; i < blocks; i++) { + if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) + return HAL_FAILED; + memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); + buf += MMCSD_BLOCK_SIZE; + startblk++; + } + return HAL_SUCCESS; + } +#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */ + return sdc_lld_read_aligned(sdcp, startblk, buf, blocks); +} + +/** + * @brief Writes one or more blocks. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * @param[in] startblk first block to write + * @param[out] buf pointer to the write buffer + * @param[in] blocks number of blocks to write + * + * @return The operation status. + * @retval HAL_SUCCESS operation succeeded. + * @retval HAL_FAILED operation failed. + * + * @notapi + */ +bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t blocks) { + +#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT + if (((unsigned)buf & 3) != 0) { + uint32_t i; + for (i = 0; i < blocks; i++) { + memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); + buf += MMCSD_BLOCK_SIZE; + if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) + return HAL_FAILED; + startblk++; + } + return HAL_SUCCESS; + } +#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */ + return sdc_lld_write_aligned(sdcp, startblk, buf, blocks); +} + +/** + * @brief Waits for card idle condition. + * + * @param[in] sdcp pointer to the @p SDCDriver object + * + * @return The operation status. + * @retval HAL_SUCCESS the operation succeeded. + * @retval HAL_FAILED the operation failed. + * + * @api + */ +bool sdc_lld_sync(SDCDriver *sdcp) { + + /* TODO: Implement.*/ + (void)sdcp; + return HAL_SUCCESS; +} + +#endif /* HAL_USE_SDC */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.h b/os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.h new file mode 100644 index 000000000..f33c4e5b5 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SDMMCv1/hal_sdc_lld.h @@ -0,0 +1,275 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SDMMCv1/sdc_lld.h + * @brief STM32 SDC subsystem low level driver header. + * + * @addtogroup SDC + * @{ + */ + +#ifndef _SDC_LLD_H_ +#define _SDC_LLD_H_ + +#if HAL_USE_SDC || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SDMMC driver enable switch. + * @details If set to @p TRUE the support for SDMMC1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SDC_USE_SDMMC1) || defined(__DOXYGEN__) +#define STM32_SDC_USE_SDMMC1 FALSE +#endif + +/** + * @brief Support for unaligned transfers. + * @note Unaligned transfers are much slower. + */ +#if !defined(STM32_SDC_SDMMC_UNALIGNED_SUPPORT) || defined(__DOXYGEN__) +#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT TRUE +#endif + +/** + * @brief Write timeout in milliseconds. + */ +#if !defined(STM32_SDC_SDMMC_WRITE_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_SDC_SDMMC_WRITE_TIMEOUT 250 +#endif + +/** + * @brief Read timeout in milliseconds. + */ +#if !defined(STM32_SDC_SDMMC_READ_TIMEOUT) || defined(__DOXYGEN__) +#define STM32_SDC_SDMMC_READ_TIMEOUT 25 +#endif + +/** + * @brief Card clock activation delay in milliseconds. + */ +#if !defined(STM32_SDC_SDMMC_CLOCK_DELAY) || defined(__DOXYGEN__) +#define STM32_SDC_SDMMC_CLOCK_DELAY 10 +#endif + +/** + * @brief SDMMC1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_SDC_SDMMC1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SDC_SDMMC1_DMA_PRIORITY 3 +#endif + +/** + * @brief SDMMC1 interrupt priority level setting. + */ +#if !defined(STM32_SDC_SDMMC1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SDC_SDMMC1_IRQ_PRIORITY 9 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_SDC_USE_SDMMC1 && !STM32_HAS_SDMMC1 +#error "SDMMC1 not present in the selected device" +#endif + +#if !STM32_SDC_USE_SDMMC1 +#error "SDC driver activated but no SDMMC peripheral assigned" +#endif + +#if !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SDC_SDMMC1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SDIO" +#endif + +#if !STM32_DMA_IS_VALID_PRIORITY(STM32_SDC_SDMMC1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SDIO" +#endif + +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if !defined(STM32_SDC_SDMMC1_DMA_STREAM) +#error "SDMMC1 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if !STM32_DMA_IS_VALID_ID(STM32_SDC_SDMMC1_DMA_STREAM, STM32_SDC_SDMMC1_DMA_MSK) +#error "invalid DMA stream associated to SDMMC1" +#endif + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of card flags. + */ +typedef uint32_t sdcmode_t; + +/** + * @brief SDC Driver condition flags type. + */ +typedef uint32_t sdcflags_t; + +/** + * @brief Type of a structure representing an SDC driver. + */ +typedef struct SDCDriver SDCDriver; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Working area for memory consuming operations. + * @note Buffer must be word aligned and big enough to store 512 bytes. + * @note It is mandatory for detecting MMC cards bigger than 2GB else it + * can be @p NULL. SD cards do NOT need it. + * @note Memory pointed by this buffer is only used by @p sdcConnect(), + * afterward it can be reused for other purposes. + */ + uint8_t *scratchpad; + /** + * @brief Bus width. + */ + sdcbusmode_t bus_width; + /* End of the mandatory fields.*/ +} SDCConfig; + +/** + * @brief @p SDCDriver specific methods. + */ +#define _sdc_driver_methods \ + _mmcsd_block_device_methods + +/** + * @extends MMCSDBlockDeviceVMT + * + * @brief @p SDCDriver virtual methods table. + */ +struct SDCDriverVMT { + _sdc_driver_methods +}; + +/** + * @brief Structure representing an SDC driver. + */ +struct SDCDriver { + /** + * @brief Virtual Methods Table. + */ + const struct SDCDriverVMT *vmt; + _mmcsd_block_device_data + /** + * @brief Current configuration data. + */ + const SDCConfig *config; + /** + * @brief Various flags regarding the mounted card. + */ + sdcmode_t cardmode; + /** + * @brief Errors flags. + */ + sdcflags_t errors; + /** + * @brief Card RCA. + */ + uint32_t rca; + /* End of the mandatory fields.*/ + /** + * @brief Thread waiting for I/O completion IRQ. + */ + thread_reference_t thread; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dma; + /** + * @brief Pointer to the SDMMC registers block. + * @note Needed for debugging aid. + */ + SDMMC_TypeDef *sdmmc; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern SDCDriver SDCD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sdc_lld_init(void); + void sdc_lld_start(SDCDriver *sdcp); + void sdc_lld_stop(SDCDriver *sdcp); + void sdc_lld_start_clk(SDCDriver *sdcp); + void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); + void sdc_lld_stop_clk(SDCDriver *sdcp); + void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); + void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); + bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, + uint32_t *resp); + bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, + uint8_t cmd, uint32_t argument); + bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, + uint8_t *buf, uint32_t blocks); + bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, + const uint8_t *buf, uint32_t blocks); + bool sdc_lld_sync(SDCDriver *sdcp); + bool sdc_lld_is_card_inserted(SDCDriver *sdcp); + bool sdc_lld_is_write_protected(SDCDriver *sdcp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SDC */ + +#endif /* _SDC_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.c b/os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.c deleted file mode 100644 index 2e1d9b051..000000000 --- a/os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.c +++ /dev/null @@ -1,885 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SDMMCv1/sdc_lld.c - * @brief STM32 SDC subsystem low level driver source. - * - * @addtogroup SDC - * @{ - */ - -#include - -#include "hal.h" - -#if HAL_USE_SDC || defined(__DOXYGEN__) - -#if !defined(STM32_SDMMCCLK) -#error "STM32_SDMMCCLK not defined" -#endif - -#if STM32_SDMMCCLK > 48000000 -#error "STM32_SDMMCCLK exceeding 48MHz" -#endif - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define SDMMC_ICR_ALL_FLAGS \ - (SDMMC_ICR_CCRCFAILC | SDMMC_ICR_DCRCFAILC | \ - SDMMC_ICR_CTIMEOUTC | SDMMC_ICR_DTIMEOUTC | \ - SDMMC_ICR_TXUNDERRC | SDMMC_ICR_RXOVERRC | \ - SDMMC_ICR_CMDRENDC | SDMMC_ICR_CMDSENTC | \ - SDMMC_ICR_DATAENDC | SDMMC_ICR_DBCKENDC | \ - SDMMC_ICR_SDIOITC) - -#define SDMMC_STA_ERROR_MASK \ - (SDMMC_STA_CCRCFAIL | SDMMC_STA_DCRCFAIL | \ - SDMMC_STA_CTIMEOUT | SDMMC_STA_DTIMEOUT | \ - SDMMC_STA_TXUNDERR | SDMMC_STA_RXOVERR) - -#define SDMMC_CLKDIV_HS (2 - 2) -#define SDMMC_CLKDIV_LS (120 - 2) - -#define SDMMC_WRITE_TIMEOUT \ - (((STM32_SDMMCCLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \ - STM32_SDC_SDMMC_WRITE_TIMEOUT) -#define SDMMC_READ_TIMEOUT \ - (((STM32_SDMMCCLK / (SDMMC_CLKDIV_HS + 2)) / 1000) * \ - STM32_SDC_SDMMC_READ_TIMEOUT) - -#define DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SDC_SDMMC1_DMA_STREAM, \ - STM32_SDC_SDMMC1_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief SDCD1 driver identifier.*/ -SDCDriver SDCD1; - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT -/** - * @brief Buffer for temporary storage during unaligned transfers. - */ -static union { - uint32_t alignment; - uint8_t buf[MMCSD_BLOCK_SIZE]; -} u; -#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */ - - -/** - * @brief SDIO default configuration. - */ -static const SDCConfig sdc_default_cfg = { - NULL, - SDC_MODE_4BIT -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Prepares to handle read transaction. - * @details Designed for read special registers from card. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[out] buf pointer to the read buffer - * @param[in] bytes number of bytes to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -static bool sdc_lld_prepare_read_bytes(SDCDriver *sdcp, - uint8_t *buf, uint32_t bytes) { - osalDbgCheck(bytes < 0x1000000); - - sdcp->sdmmc->DTIMER = SDMMC_READ_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, bytes / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; - sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE | - SDMMC_MASK_DTIMEOUTIE | - SDMMC_MASK_RXOVERRIE | - SDMMC_MASK_DATAENDIE; - sdcp->sdmmc->DLEN = bytes; - - /* Transaction starts just after DTEN bit setting.*/ - sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DTDIR | - SDMMC_DCTRL_DTMODE | /* multibyte data transfer */ - SDMMC_DCTRL_DMAEN | - SDMMC_DCTRL_DTEN; - - return HAL_SUCCESS; -} - -/** - * @brief Prepares card to handle read transaction. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[in] n number of blocks to read - * @param[in] resp pointer to the response buffer - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -static bool sdc_lld_prepare_read(SDCDriver *sdcp, uint32_t startblk, - uint32_t n, uint32_t *resp) { - - /* Driver handles data in 512 bytes blocks (just like HC cards). But if we - have not HC card than we must convert address from blocks to bytes.*/ - if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) - startblk *= MMCSD_BLOCK_SIZE; - - if (n > 1) { - /* Send read multiple blocks command to card.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - else{ - /* Send read single block command.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_READ_SINGLE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - - return HAL_SUCCESS; -} - -/** - * @brief Prepares card to handle write transaction. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[in] n number of blocks to write - * @param[in] resp pointer to the response buffer - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -static bool sdc_lld_prepare_write(SDCDriver *sdcp, uint32_t startblk, - uint32_t n, uint32_t *resp) { - - /* Driver handles data in 512 bytes blocks (just like HC cards). But if we - have not HC card than we must convert address from blocks to bytes.*/ - if (!(sdcp->cardmode & SDC_MODE_HIGH_CAPACITY)) - startblk *= MMCSD_BLOCK_SIZE; - - if (n > 1) { - /* Write multiple blocks command.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - else{ - /* Write single block command.*/ - if (sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_WRITE_BLOCK, - startblk, resp) || MMCSD_R1_ERROR(resp[0])) - return HAL_FAILED; - } - - return HAL_SUCCESS; -} - -/** - * @brief Wait end of data transaction and performs finalizations. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] n number of blocks in transaction - * @param[in] resp pointer to the response buffer - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - */ -static bool sdc_lld_wait_transaction_end(SDCDriver *sdcp, uint32_t n, - uint32_t *resp) { - - /* Note the mask is checked before going to sleep because the interrupt - may have occurred before reaching the critical zone.*/ - osalSysLock(); - if (sdcp->sdmmc->MASK != 0) - osalThreadSuspendS(&sdcp->thread); - if ((sdcp->sdmmc->STA & SDMMC_STA_DATAEND) == 0) { - osalSysUnlock(); - return HAL_FAILED; - } - - /* Waits for transfer completion at DMA level, then the stream is - disabled and cleared.*/ - dmaWaitCompletion(sdcp->dma); - - sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; - sdcp->sdmmc->DCTRL = 0; - osalSysUnlock(); - - /* Finalize transaction.*/ - if (n > 1) - return sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); - - return HAL_SUCCESS; -} - -/** - * @brief Gets SDC errors. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] sta value of the STA register - * - * @notapi - */ -static void sdc_lld_collect_errors(SDCDriver *sdcp, uint32_t sta) { - uint32_t errors = SDC_NO_ERROR; - - if (sta & SDMMC_STA_CCRCFAIL) - errors |= SDC_CMD_CRC_ERROR; - if (sta & SDMMC_STA_DCRCFAIL) - errors |= SDC_DATA_CRC_ERROR; - if (sta & SDMMC_STA_CTIMEOUT) - errors |= SDC_COMMAND_TIMEOUT; - if (sta & SDMMC_STA_DTIMEOUT) - errors |= SDC_DATA_TIMEOUT; - if (sta & SDMMC_STA_TXUNDERR) - errors |= SDC_TX_UNDERRUN; - if (sta & SDMMC_STA_RXOVERR) - errors |= SDC_RX_OVERRUN; -/* if (sta & SDMMC_STA_STBITERR) - errors |= SDC_STARTBIT_ERROR;*/ - - sdcp->errors |= errors; -} - -/** - * @brief Performs clean transaction stopping in case of errors. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] n number of blocks in transaction - * @param[in] resp pointer to the response buffer - * - * @notapi - */ -static void sdc_lld_error_cleanup(SDCDriver *sdcp, - uint32_t n, - uint32_t *resp) { - uint32_t sta = sdcp->sdmmc->STA; - - dmaStreamDisable(sdcp->dma); - sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; - sdcp->sdmmc->MASK = 0; - sdcp->sdmmc->DCTRL = 0; - sdc_lld_collect_errors(sdcp, sta); - - if (n > 1) - sdc_lld_send_cmd_short_crc(sdcp, MMCSD_CMD_STOP_TRANSMISSION, 0, resp); -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if !defined(STM32_SDMMC1_HANDLER) -#error "STM32_SDMMC1_HANDLER not defined" -#endif - -/** - * @brief SDIO IRQ handler. - * @details It just wakes transaction thread. All error handling performs in - * that thread. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_SDMMC1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - osalSysLockFromISR(); - - /* Disables the source but the status flags are not reset because the - read/write functions needs to check them.*/ - SDMMC1->MASK = 0; - - osalThreadResumeI(&SDCD1.thread, MSG_OK); - - osalSysUnlockFromISR(); - - OSAL_IRQ_EPILOGUE(); -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SDC driver initialization. - * - * @notapi - */ -void sdc_lld_init(void) { - - sdcObjectInit(&SDCD1); - SDCD1.thread = NULL; - SDCD1.dma = STM32_DMA_STREAM(STM32_SDC_SDMMC1_DMA_STREAM); - SDCD1.sdmmc = SDMMC1; -} - -/** - * @brief Configures and activates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start(SDCDriver *sdcp) { - - /* Checking configuration, using a default if NULL has been passed.*/ - if (sdcp->config == NULL) { - sdcp->config = &sdc_default_cfg; - } - - sdcp->dmamode = STM32_DMA_CR_CHSEL(DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SDC_SDMMC1_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_WORD | - STM32_DMA_CR_MSIZE_WORD | - STM32_DMA_CR_MINC; - -#if STM32_DMA_ADVANCED - sdcp->dmamode |= STM32_DMA_CR_PFCTRL | - STM32_DMA_CR_PBURST_INCR4 | - STM32_DMA_CR_MBURST_INCR4; -#endif - - if (sdcp->state == BLK_STOP) { - /* Note, the DMA must be enabled before the IRQs.*/ - bool b; - b = dmaStreamAllocate(sdcp->dma, STM32_SDC_SDMMC1_IRQ_PRIORITY, NULL, NULL); - osalDbgAssert(!b, "stream already allocated"); - dmaStreamSetPeripheral(sdcp->dma, &sdcp->sdmmc->FIFO); -#if STM32_DMA_ADVANCED - dmaStreamSetFIFO(sdcp->dma, STM32_DMA_FCR_DMDIS | STM32_DMA_FCR_FTH_FULL); -#endif - nvicEnableVector(STM32_SDMMC1_NUMBER, STM32_SDC_SDMMC1_IRQ_PRIORITY); - rccEnableSDMMC1(FALSE); - } - - /* Configuration, card clock is initially stopped.*/ - sdcp->sdmmc->POWER = 0; - sdcp->sdmmc->CLKCR = 0; - sdcp->sdmmc->DCTRL = 0; - sdcp->sdmmc->DTIMER = 0; -} - -/** - * @brief Deactivates the SDC peripheral. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop(SDCDriver *sdcp) { - - if (sdcp->state != BLK_STOP) { - - /* SDIO deactivation.*/ - sdcp->sdmmc->POWER = 0; - sdcp->sdmmc->CLKCR = 0; - sdcp->sdmmc->DCTRL = 0; - sdcp->sdmmc->DTIMER = 0; - - /* Clock deactivation.*/ - nvicDisableVector(STM32_SDMMC1_NUMBER); - dmaStreamRelease(sdcp->dma); - rccDisableSDMMC1(FALSE); - } -} - -/** - * @brief Starts the SDIO clock and sets it to init mode (400kHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_start_clk(SDCDriver *sdcp) { - - /* Initial clock setting: 400kHz, 1bit mode.*/ - sdcp->sdmmc->CLKCR = SDMMC_CLKDIV_LS; - sdcp->sdmmc->POWER |= SDMMC_POWER_PWRCTRL_0 | SDMMC_POWER_PWRCTRL_1; - sdcp->sdmmc->CLKCR |= SDMMC_CLKCR_CLKEN; - - /* Clock activation delay.*/ - osalThreadSleep(OSAL_MS2ST(STM32_SDC_SDMMC_CLOCK_DELAY)); -} - -/** - * @brief Sets the SDIO clock to data mode (25MHz or less). - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] clk the clock mode - * - * @notapi - */ -void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk) { -#if 0 - if (SDC_CLK_50MHz == clk) { - sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | - SDMMC_CLKDIV_HS | SDMMC_CLKCR_BYPASS; - } - else - sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS; -#else - (void)clk; - - sdcp->sdmmc->CLKCR = (sdcp->sdmmc->CLKCR & 0xFFFFFF00U) | SDMMC_CLKDIV_HS; -#endif -} - -/** - * @brief Stops the SDIO clock. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @notapi - */ -void sdc_lld_stop_clk(SDCDriver *sdcp) { - - sdcp->sdmmc->CLKCR = 0; - sdcp->sdmmc->POWER = 0; -} - -/** - * @brief Switches the bus to 4 bits mode. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] mode bus mode - * - * @notapi - */ -void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode) { - uint32_t clk = sdcp->sdmmc->CLKCR & ~SDMMC_CLKCR_WIDBUS; - - switch (mode) { - case SDC_MODE_1BIT: - sdcp->sdmmc->CLKCR = clk; - break; - case SDC_MODE_4BIT: - sdcp->sdmmc->CLKCR = clk | SDMMC_CLKCR_WIDBUS_0; - break; - case SDC_MODE_8BIT: - sdcp->sdmmc->CLKCR = clk | SDMMC_CLKCR_WIDBUS_1; - break; - } -} - -/** - * @brief Sends an SDIO command with no response expected. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * - * @notapi - */ -void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg) { - - sdcp->sdmmc->ARG = arg; - sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_CPSMEN; - while ((sdcp->sdmmc->STA & SDMMC_STA_CMDSENT) == 0) - ; - sdcp->sdmmc->ICR = SDMMC_ICR_CMDSENTC; -} - -/** - * @brief Sends an SDIO command with a short response expected. - * @note The CRC is not verified. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (one word) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - sdcp->sdmmc->ARG = arg; - sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN; - while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | - SDMMC_STA_CCRCFAIL)) == 0) - ; - sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | - SDMMC_STA_CCRCFAIL); - if ((sta & (SDMMC_STA_CTIMEOUT)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return HAL_FAILED; - } - *resp = sdcp->sdmmc->RESP1; - return HAL_SUCCESS; -} - -/** - * @brief Sends an SDIO command with a short response expected and CRC. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (one word) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - sdcp->sdmmc->ARG = arg; - sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_CPSMEN; - while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | - SDMMC_STA_CCRCFAIL)) == 0) - ; - sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | SDMMC_STA_CCRCFAIL); - if ((sta & (SDMMC_STA_CTIMEOUT | SDMMC_STA_CCRCFAIL)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return HAL_FAILED; - } - *resp = sdcp->sdmmc->RESP1; - return HAL_SUCCESS; -} - -/** - * @brief Sends an SDIO command with a long response expected and CRC. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] cmd card command - * @param[in] arg command argument - * @param[out] resp pointer to the response buffer (four words) - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp) { - uint32_t sta; - - (void)sdcp; - - sdcp->sdmmc->ARG = arg; - sdcp->sdmmc->CMD = (uint32_t)cmd | SDMMC_CMD_WAITRESP_0 | SDMMC_CMD_WAITRESP_1 | - SDMMC_CMD_CPSMEN; - while (((sta = sdcp->sdmmc->STA) & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | - SDMMC_STA_CCRCFAIL)) == 0) - ; - sdcp->sdmmc->ICR = sta & (SDMMC_STA_CMDREND | SDMMC_STA_CTIMEOUT | - SDMMC_STA_CCRCFAIL); - if ((sta & (SDMMC_STA_ERROR_MASK)) != 0) { - sdc_lld_collect_errors(sdcp, sta); - return HAL_FAILED; - } - /* Save bytes in reverse order because MSB in response comes first.*/ - *resp++ = sdcp->sdmmc->RESP4; - *resp++ = sdcp->sdmmc->RESP3; - *resp++ = sdcp->sdmmc->RESP2; - *resp = sdcp->sdmmc->RESP1; - return HAL_SUCCESS; -} - -/** - * @brief Reads special registers using data bus. - * @details Needs only during card detection procedure. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[out] buf pointer to the read buffer - * @param[in] bytes number of bytes to read - * @param[in] cmd card command - * @param[in] arg argument for command - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, - uint8_t cmd, uint32_t arg) { - uint32_t resp[1]; - - if(sdc_lld_prepare_read_bytes(sdcp, buf, bytes)) - goto error; - - if (sdc_lld_send_cmd_short_crc(sdcp, cmd, arg, resp) - || MMCSD_R1_ERROR(resp[0])) - goto error; - - if (sdc_lld_wait_transaction_end(sdcp, 1, resp)) - goto error; - - return HAL_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, 1, resp); - return HAL_FAILED; -} - -/** - * @brief Reads one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[out] buf pointer to the read buffer - * @param[in] blocks number of blocks to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_read_aligned(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t blocks) { - uint32_t resp[1]; - - osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); - - sdcp->sdmmc->DTIMER = SDMMC_READ_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for reading.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_P2M); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; - sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE | - SDMMC_MASK_DTIMEOUTIE | - SDMMC_MASK_RXOVERRIE | - SDMMC_MASK_DATAENDIE; - sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE; - - /* Transaction starts just after DTEN bit setting.*/ - sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DTDIR | - SDMMC_DCTRL_DBLOCKSIZE_3 | - SDMMC_DCTRL_DBLOCKSIZE_0 | - SDMMC_DCTRL_DMAEN | - SDMMC_DCTRL_DTEN; - - if (sdc_lld_prepare_read(sdcp, startblk, blocks, resp) == TRUE) - goto error; - - if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) - goto error; - - return HAL_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, blocks, resp); - return HAL_FAILED; -} - -/** - * @brief Writes one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to write - * @param[out] buf pointer to the write buffer - * @param[in] n number of blocks to write - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_write_aligned(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t blocks) { - uint32_t resp[1]; - - osalDbgCheck(blocks < 0x1000000 / MMCSD_BLOCK_SIZE); - - sdcp->sdmmc->DTIMER = SDMMC_WRITE_TIMEOUT; - - /* Checks for errors and waits for the card to be ready for writing.*/ - if (_sdc_wait_for_transfer_state(sdcp)) - return HAL_FAILED; - - /* Prepares the DMA channel for writing.*/ - dmaStreamSetMemory0(sdcp->dma, buf); - dmaStreamSetTransactionSize(sdcp->dma, - (blocks * MMCSD_BLOCK_SIZE) / sizeof (uint32_t)); - dmaStreamSetMode(sdcp->dma, sdcp->dmamode | STM32_DMA_CR_DIR_M2P); - dmaStreamEnable(sdcp->dma); - - /* Setting up data transfer.*/ - sdcp->sdmmc->ICR = SDMMC_ICR_ALL_FLAGS; - sdcp->sdmmc->MASK = SDMMC_MASK_DCRCFAILIE | - SDMMC_MASK_DTIMEOUTIE | - SDMMC_MASK_TXUNDERRIE | - SDMMC_MASK_DATAENDIE; - sdcp->sdmmc->DLEN = blocks * MMCSD_BLOCK_SIZE; - - /* Talk to card what we want from it.*/ - if (sdc_lld_prepare_write(sdcp, startblk, blocks, resp) == TRUE) - goto error; - - /* Transaction starts just after DTEN bit setting.*/ - sdcp->sdmmc->DCTRL = SDMMC_DCTRL_DBLOCKSIZE_3 | - SDMMC_DCTRL_DBLOCKSIZE_0 | - SDMMC_DCTRL_DMAEN | - SDMMC_DCTRL_DTEN; - - if (sdc_lld_wait_transaction_end(sdcp, blocks, resp) == TRUE) - goto error; - - return HAL_SUCCESS; - -error: - sdc_lld_error_cleanup(sdcp, blocks, resp); - return HAL_FAILED; -} - -/** - * @brief Reads one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to read - * @param[out] buf pointer to the read buffer - * @param[in] blocks number of blocks to read - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t blocks) { - -#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < blocks; i++) { - if (sdc_lld_read_aligned(sdcp, startblk, u.buf, 1)) - return HAL_FAILED; - memcpy(buf, u.buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - startblk++; - } - return HAL_SUCCESS; - } -#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */ - return sdc_lld_read_aligned(sdcp, startblk, buf, blocks); -} - -/** - * @brief Writes one or more blocks. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * @param[in] startblk first block to write - * @param[out] buf pointer to the write buffer - * @param[in] blocks number of blocks to write - * - * @return The operation status. - * @retval HAL_SUCCESS operation succeeded. - * @retval HAL_FAILED operation failed. - * - * @notapi - */ -bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t blocks) { - -#if STM32_SDC_SDMMC_UNALIGNED_SUPPORT - if (((unsigned)buf & 3) != 0) { - uint32_t i; - for (i = 0; i < blocks; i++) { - memcpy(u.buf, buf, MMCSD_BLOCK_SIZE); - buf += MMCSD_BLOCK_SIZE; - if (sdc_lld_write_aligned(sdcp, startblk, u.buf, 1)) - return HAL_FAILED; - startblk++; - } - return HAL_SUCCESS; - } -#endif /* STM32_SDC_SDMMC_UNALIGNED_SUPPORT */ - return sdc_lld_write_aligned(sdcp, startblk, buf, blocks); -} - -/** - * @brief Waits for card idle condition. - * - * @param[in] sdcp pointer to the @p SDCDriver object - * - * @return The operation status. - * @retval HAL_SUCCESS the operation succeeded. - * @retval HAL_FAILED the operation failed. - * - * @api - */ -bool sdc_lld_sync(SDCDriver *sdcp) { - - /* TODO: Implement.*/ - (void)sdcp; - return HAL_SUCCESS; -} - -#endif /* HAL_USE_SDC */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.h b/os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.h deleted file mode 100644 index f33c4e5b5..000000000 --- a/os/hal/ports/STM32/LLD/SDMMCv1/sdc_lld.h +++ /dev/null @@ -1,275 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SDMMCv1/sdc_lld.h - * @brief STM32 SDC subsystem low level driver header. - * - * @addtogroup SDC - * @{ - */ - -#ifndef _SDC_LLD_H_ -#define _SDC_LLD_H_ - -#if HAL_USE_SDC || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief SDMMC driver enable switch. - * @details If set to @p TRUE the support for SDMMC1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SDC_USE_SDMMC1) || defined(__DOXYGEN__) -#define STM32_SDC_USE_SDMMC1 FALSE -#endif - -/** - * @brief Support for unaligned transfers. - * @note Unaligned transfers are much slower. - */ -#if !defined(STM32_SDC_SDMMC_UNALIGNED_SUPPORT) || defined(__DOXYGEN__) -#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT TRUE -#endif - -/** - * @brief Write timeout in milliseconds. - */ -#if !defined(STM32_SDC_SDMMC_WRITE_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_SDC_SDMMC_WRITE_TIMEOUT 250 -#endif - -/** - * @brief Read timeout in milliseconds. - */ -#if !defined(STM32_SDC_SDMMC_READ_TIMEOUT) || defined(__DOXYGEN__) -#define STM32_SDC_SDMMC_READ_TIMEOUT 25 -#endif - -/** - * @brief Card clock activation delay in milliseconds. - */ -#if !defined(STM32_SDC_SDMMC_CLOCK_DELAY) || defined(__DOXYGEN__) -#define STM32_SDC_SDMMC_CLOCK_DELAY 10 -#endif - -/** - * @brief SDMMC1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_SDC_SDMMC1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SDC_SDMMC1_DMA_PRIORITY 3 -#endif - -/** - * @brief SDMMC1 interrupt priority level setting. - */ -#if !defined(STM32_SDC_SDMMC1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SDC_SDMMC1_IRQ_PRIORITY 9 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_SDC_USE_SDMMC1 && !STM32_HAS_SDMMC1 -#error "SDMMC1 not present in the selected device" -#endif - -#if !STM32_SDC_USE_SDMMC1 -#error "SDC driver activated but no SDMMC peripheral assigned" -#endif - -#if !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SDC_SDMMC1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SDIO" -#endif - -#if !STM32_DMA_IS_VALID_PRIORITY(STM32_SDC_SDMMC1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SDIO" -#endif - -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if !defined(STM32_SDC_SDMMC1_DMA_STREAM) -#error "SDMMC1 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if !STM32_DMA_IS_VALID_ID(STM32_SDC_SDMMC1_DMA_STREAM, STM32_SDC_SDMMC1_DMA_MSK) -#error "invalid DMA stream associated to SDMMC1" -#endif - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of card flags. - */ -typedef uint32_t sdcmode_t; - -/** - * @brief SDC Driver condition flags type. - */ -typedef uint32_t sdcflags_t; - -/** - * @brief Type of a structure representing an SDC driver. - */ -typedef struct SDCDriver SDCDriver; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Working area for memory consuming operations. - * @note Buffer must be word aligned and big enough to store 512 bytes. - * @note It is mandatory for detecting MMC cards bigger than 2GB else it - * can be @p NULL. SD cards do NOT need it. - * @note Memory pointed by this buffer is only used by @p sdcConnect(), - * afterward it can be reused for other purposes. - */ - uint8_t *scratchpad; - /** - * @brief Bus width. - */ - sdcbusmode_t bus_width; - /* End of the mandatory fields.*/ -} SDCConfig; - -/** - * @brief @p SDCDriver specific methods. - */ -#define _sdc_driver_methods \ - _mmcsd_block_device_methods - -/** - * @extends MMCSDBlockDeviceVMT - * - * @brief @p SDCDriver virtual methods table. - */ -struct SDCDriverVMT { - _sdc_driver_methods -}; - -/** - * @brief Structure representing an SDC driver. - */ -struct SDCDriver { - /** - * @brief Virtual Methods Table. - */ - const struct SDCDriverVMT *vmt; - _mmcsd_block_device_data - /** - * @brief Current configuration data. - */ - const SDCConfig *config; - /** - * @brief Various flags regarding the mounted card. - */ - sdcmode_t cardmode; - /** - * @brief Errors flags. - */ - sdcflags_t errors; - /** - * @brief Card RCA. - */ - uint32_t rca; - /* End of the mandatory fields.*/ - /** - * @brief Thread waiting for I/O completion IRQ. - */ - thread_reference_t thread; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dma; - /** - * @brief Pointer to the SDMMC registers block. - * @note Needed for debugging aid. - */ - SDMMC_TypeDef *sdmmc; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if !defined(__DOXYGEN__) -extern SDCDriver SDCD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void sdc_lld_init(void); - void sdc_lld_start(SDCDriver *sdcp); - void sdc_lld_stop(SDCDriver *sdcp); - void sdc_lld_start_clk(SDCDriver *sdcp); - void sdc_lld_set_data_clk(SDCDriver *sdcp, sdcbusclk_t clk); - void sdc_lld_stop_clk(SDCDriver *sdcp); - void sdc_lld_set_bus_mode(SDCDriver *sdcp, sdcbusmode_t mode); - void sdc_lld_send_cmd_none(SDCDriver *sdcp, uint8_t cmd, uint32_t arg); - bool sdc_lld_send_cmd_short(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_send_cmd_short_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_send_cmd_long_crc(SDCDriver *sdcp, uint8_t cmd, uint32_t arg, - uint32_t *resp); - bool sdc_lld_read_special(SDCDriver *sdcp, uint8_t *buf, size_t bytes, - uint8_t cmd, uint32_t argument); - bool sdc_lld_read(SDCDriver *sdcp, uint32_t startblk, - uint8_t *buf, uint32_t blocks); - bool sdc_lld_write(SDCDriver *sdcp, uint32_t startblk, - const uint8_t *buf, uint32_t blocks); - bool sdc_lld_sync(SDCDriver *sdcp); - bool sdc_lld_is_card_inserted(SDCDriver *sdcp); - bool sdc_lld_is_write_protected(SDCDriver *sdcp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SDC */ - -#endif /* _SDC_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.c b/os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.c new file mode 100644 index 000000000..e1f75c50f --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.c @@ -0,0 +1,577 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file i2s_lld.c + * @brief STM32 I2S subsystem low level driver source. + * + * @addtogroup I2S + * @{ + */ + +#include "hal.h" + +#if HAL_USE_I2S || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define I2S1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_RX_DMA_STREAM, \ + STM32_SPI1_RX_DMA_CHN) + +#define I2S1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_TX_DMA_STREAM, \ + STM32_SPI1_TX_DMA_CHN) + +#define I2S2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_RX_DMA_STREAM, \ + STM32_SPI2_RX_DMA_CHN) + +#define I2S2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_TX_DMA_STREAM, \ + STM32_SPI2_TX_DMA_CHN) + +#define I2S3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_RX_DMA_STREAM, \ + STM32_SPI3_RX_DMA_CHN) + +#define I2S3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_TX_DMA_STREAM, \ + STM32_SPI3_TX_DMA_CHN) + +/* + * Static I2S settings for I2S1. + */ +#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG 0 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 +#endif +#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ + SPI_I2SCFGR_I2SCFG_0) +#endif +#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ + +/* + * Static I2S settings for I2S2. + */ +#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG 0 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 +#endif +#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ + SPI_I2SCFGR_I2SCFG_0) +#endif +#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ + +/* + * Static I2S settings for I2S3. + */ +#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG 0 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 +#endif +#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ + SPI_I2SCFGR_I2SCFG_0) +#endif +#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2S1 driver identifier.*/ +#if STM32_I2S_USE_SPI1 || defined(__DOXYGEN__) +I2SDriver I2SD1; +#endif + +/** @brief I2S2 driver identifier.*/ +#if STM32_I2S_USE_SPI2 || defined(__DOXYGEN__) +I2SDriver I2SD2; +#endif + +/** @brief I2S3 driver identifier.*/ +#if STM32_I2S_USE_SPI3 || defined(__DOXYGEN__) +I2SDriver I2SD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) || \ + STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) || \ + STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) +/** + * @brief Shared end-of-rx service routine. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void i2s_lld_serve_rx_interrupt(I2SDriver *i2sp, uint32_t flags) { + + (void)i2sp; + + /* DMA errors handling.*/ +#if defined(STM32_I2S_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2S_DMA_ERROR_HOOK(i2sp); + } +#endif + + /* Callbacks handling, note it is portable code defined in the high + level driver.*/ + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _i2s_isr_full_code(i2sp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _i2s_isr_half_code(i2sp); + } +} +#endif + +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) || \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) || \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) +/** + * @brief Shared end-of-tx service routine. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void i2s_lld_serve_tx_interrupt(I2SDriver *i2sp, uint32_t flags) { + + (void)i2sp; + + /* DMA errors handling.*/ +#if defined(STM32_I2S_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2S_DMA_ERROR_HOOK(i2sp); + } +#endif + + /* Callbacks handling, note it is portable code defined in the high + level driver.*/ + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _i2s_isr_full_code(i2sp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _i2s_isr_half_code(i2sp); + } +} +#endif + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2S driver initialization. + * + * @notapi + */ +void i2s_lld_init(void) { + +#if STM32_I2S_USE_SPI1 + i2sObjectInit(&I2SD1); + I2SD1.spi = SPI1; + I2SD1.cfg = STM32_I2S1_CFGR_CFG; +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) + I2SD1.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI1_RX_DMA_STREAM); + I2SD1.rxdmamode = STM32_DMA_CR_CHSEL(I2S1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD1.dmarx = NULL; + I2SD1.rxdmamode = 0; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) + I2SD1.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI1_TX_DMA_STREAM); + I2SD1.txdmamode = STM32_DMA_CR_CHSEL(I2S1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD1.dmatx = NULL; + I2SD1.txdmamode = 0; +#endif +#endif + +#if STM32_I2S_USE_SPI2 + i2sObjectInit(&I2SD2); + I2SD2.spi = SPI2; + I2SD2.cfg = STM32_I2S2_CFGR_CFG; +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) + I2SD2.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI2_RX_DMA_STREAM); + I2SD2.rxdmamode = STM32_DMA_CR_CHSEL(I2S2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD2.dmarx = NULL; + I2SD2.rxdmamode = 0; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) + I2SD2.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI2_TX_DMA_STREAM); + I2SD2.txdmamode = STM32_DMA_CR_CHSEL(I2S2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD2.dmatx = NULL; + I2SD2.txdmamode = 0; +#endif +#endif + +#if STM32_I2S_USE_SPI3 + i2sObjectInit(&I2SD3); + I2SD3.spi = SPI3; + I2SD3.cfg = STM32_I2S3_CFGR_CFG; +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) + I2SD3.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI3_RX_DMA_STREAM); + I2SD3.rxdmamode = STM32_DMA_CR_CHSEL(I2S3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD3.dmarx = NULL; + I2SD3.rxdmamode = 0; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) + I2SD3.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI3_TX_DMA_STREAM); + I2SD3.txdmamode = STM32_DMA_CR_CHSEL(I2S3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD3.dmatx = NULL; + I2SD3.txdmamode = 0; +#endif +#endif +} + +/** + * @brief Configures and activates the I2S peripheral. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_start(I2SDriver *i2sp) { + + /* If in stopped state then enables the SPI and DMA clocks.*/ + if (i2sp->state == I2S_STOP) { + +#if STM32_I2S_USE_SPI1 + if (&I2SD1 == i2sp) { + bool b; + + /* Enabling I2S unit clock.*/ + rccEnableSPI1(FALSE); + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) + b = dmaStreamAllocate(i2sp->dmarx, + STM32_I2S_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_RXDMAEN; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) + b = dmaStreamAllocate(i2sp->dmatx, + STM32_I2S_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_TXDMAEN; +#endif + } +#endif + +#if STM32_I2S_USE_SPI2 + if (&I2SD2 == i2sp) { + bool b; + + /* Enabling I2S unit clock.*/ + rccEnableSPI2(FALSE); + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) + b = dmaStreamAllocate(i2sp->dmarx, + STM32_I2S_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_RXDMAEN; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) + b = dmaStreamAllocate(i2sp->dmatx, + STM32_I2S_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_TXDMAEN; +#endif + } +#endif + +#if STM32_I2S_USE_SPI3 + if (&I2SD3 == i2sp) { + bool b; + + /* Enabling I2S unit clock.*/ + rccEnableSPI3(FALSE); + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) + b = dmaStreamAllocate(i2sp->dmarx, + STM32_I2S_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_RXDMAEN; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) + b = dmaStreamAllocate(i2sp->dmatx, + STM32_I2S_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_TXDMAEN; +#endif + } +#endif + } + + /* I2S (re)configuration.*/ + i2sp->spi->I2SPR = i2sp->config->i2spr; + i2sp->spi->I2SCFGR = i2sp->config->i2scfgr | i2sp->cfg | SPI_I2SCFGR_I2SMOD; +} + +/** + * @brief Deactivates the I2S peripheral. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_stop(I2SDriver *i2sp) { + + /* If in ready state then disables the SPI clock.*/ + if (i2sp->state == I2S_READY) { + + /* SPI disable.*/ + i2sp->spi->CR2 = 0; + if (NULL != i2sp->dmarx) + dmaStreamRelease(i2sp->dmarx); + if (NULL != i2sp->dmatx) + dmaStreamRelease(i2sp->dmatx); + +#if STM32_I2S_USE_SPI1 + if (&I2SD1 == i2sp) + rccDisableSPI1(FALSE); +#endif + +#if STM32_I2S_USE_SPI2 + if (&I2SD2 == i2sp) + rccDisableSPI2(FALSE); +#endif + +#if STM32_I2S_USE_SPI3 + if (&I2SD3 == i2sp) + rccDisableSPI3(FALSE); +#endif + } +} + +/** + * @brief Starts a I2S data exchange. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_start_exchange(I2SDriver *i2sp) { + size_t size = i2sp->config->size; + + /* In 32 bit modes the DMA has to perform double operations because fetches + are always performed using 16 bit accesses. + DATLEN CHLEN SIZE + 00 (16) 0 (16) 16 + 00 (16) 1 (32) 16 + 01 (24) X 32 + 10 (32) X 32 + 11 (NA) X NA + */ + if ((i2sp->config->i2scfgr & SPI_I2SCFGR_DATLEN) != 0) + size *= 2; + + /* RX DMA setup.*/ + if (NULL != i2sp->dmarx) { + dmaStreamSetMode(i2sp->dmarx, i2sp->rxdmamode); + dmaStreamSetPeripheral(i2sp->dmarx, &i2sp->spi->DR); + dmaStreamSetMemory0(i2sp->dmarx, i2sp->config->rx_buffer); + dmaStreamSetTransactionSize(i2sp->dmarx, size); + dmaStreamEnable(i2sp->dmarx); + } + + /* TX DMA setup.*/ + if (NULL != i2sp->dmatx) { + dmaStreamSetMode(i2sp->dmatx, i2sp->txdmamode); + dmaStreamSetPeripheral(i2sp->dmatx, &i2sp->spi->DR); + dmaStreamSetMemory0(i2sp->dmatx, i2sp->config->tx_buffer); + dmaStreamSetTransactionSize(i2sp->dmatx, size); + dmaStreamEnable(i2sp->dmatx); + } + + /* Starting transfer.*/ + i2sp->spi->I2SCFGR |= SPI_I2SCFGR_I2SE; +} + +/** + * @brief Stops the ongoing data exchange. + * @details The ongoing data exchange, if any, is stopped, if the driver + * was not active the function does nothing. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_stop_exchange(I2SDriver *i2sp) { + + /* Stop TX DMA, if enabled.*/ + if (NULL != i2sp->dmatx) { + dmaStreamDisable(i2sp->dmatx); + + /* From the RM: To switch off the I2S, by clearing I2SE, it is mandatory + to wait for TXE = 1 and BSY = 0.*/ + while ((i2sp->spi->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE) + ; + } + + /* Stop SPI/I2S peripheral.*/ + i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE; + + /* Stop RX DMA, if enabled.*/ + if (NULL != i2sp->dmarx) + dmaStreamDisable(i2sp->dmarx); +} + +#endif /* HAL_USE_I2S */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.h b/os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.h new file mode 100644 index 000000000..0941ae3a8 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv1/hal_i2s_lld.h @@ -0,0 +1,432 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file i2s_lld.h + * @brief STM32 I2S subsystem low level driver header. + * + * @addtogroup I2S + * @{ + */ + +#ifndef _I2S_LLD_H_ +#define _I2S_LLD_H_ + +#if HAL_USE_I2S || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Static I2S modes + * @{ + */ +#define STM32_I2S_MODE_SLAVE 0 +#define STM32_I2S_MODE_MASTER 1 +#define STM32_I2S_MODE_RX 2 +#define STM32_I2S_MODE_TX 4 +#define STM32_I2S_MODE_RXTX (STM32_I2S_MODE_RX | \ + STM32_I2S_MODE_TX) +/** @} */ + +/** + * @name Mode checks + * @{ + */ +#define STM32_I2S_IS_MASTER(mode) ((mode) & STM32_I2S_MODE_MASTER) +#define STM32_I2S_RX_ENABLED(mode) ((mode) & STM32_I2S_MODE_RX) +#define STM32_I2S_TX_ENABLED(mode) ((mode) & STM32_I2S_MODE_TX) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2S1 driver enable switch. + * @details If set to @p TRUE the support for I2S1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_I2S_USE_SPI1) || defined(__DOXYGEN__) +#define STM32_I2S_USE_SPI1 FALSE +#endif + +/** + * @brief I2S2 driver enable switch. + * @details If set to @p TRUE the support for I2S2 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_I2S_USE_SPI2) || defined(__DOXYGEN__) +#define STM32_I2S_USE_SPI2 FALSE +#endif + +/** + * @brief I2S3 driver enable switch. + * @details If set to @p TRUE the support for I2S3 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_I2S_USE_SPI3) || defined(__DOXYGEN__) +#define STM32_I2S_USE_SPI3 FALSE +#endif + +/** + * @brief I2S1 mode. + */ +#if !defined(STM32_I2S_SPI1_MODE) || defined(__DOXYGEN__) +#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#endif + +/** + * @brief I2S2 mode. + */ +#if !defined(STM32_I2S_SPI2_MODE) || defined(__DOXYGEN__) +#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#endif + +/** + * @brief I2S3 mode. + */ +#if !defined(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) +#define STM32_I2S_SPI3_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#endif + +/** + * @brief I2S1 interrupt priority level setting. + */ +#if !defined(STM32_I2S_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI1_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2S2 interrupt priority level setting. + */ +#if !defined(STM32_I2S_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI2_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2S3 interrupt priority level setting. + */ +#if !defined(STM32_I2S_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI3_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2S1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_I2S_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI1_DMA_PRIORITY 1 +#endif + +/** + * @brief I2S2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_I2S_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI2_DMA_PRIORITY 1 +#endif + +/** + * @brief I2S3 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_I2S_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI3_DMA_PRIORITY 1 +#endif + +/** + * @brief I2S DMA error hook. + */ +#if !defined(STM32_I2S_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_I2S_USE_SPI1 && !STM32_SPI1_SUPPORTS_I2S +#error "SPI1 does not support I2S mode" +#endif + +#if STM32_I2S_USE_SPI2 && !STM32_SPI2_SUPPORTS_I2S +#error "SPI2 does not support I2S mode" +#endif + +#if STM32_I2S_USE_SPI3 && !STM32_SPI3_SUPPORTS_I2S +#error "SPI3 does not support I2S mode" +#endif + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) && \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) +#error "I2S1 RX and TX mode not supported in this driver implementation" +#endif + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) && \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) +#error "I2S2 RX and TX mode not supported in this driver implementation" +#endif + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) && \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) +#error "I2S3 RX and TX mode not supported in this driver implementation" +#endif + +#if STM32_I2S_USE_SPI1 && !STM32_HAS_SPI1 +#error "SPI1 not present in the selected device" +#endif + +#if STM32_I2S_USE_SPI2 && !STM32_HAS_SPI2 +#error "SPI2 not present in the selected device" +#endif + +#if STM32_I2S_USE_SPI3 && !STM32_HAS_SPI3 +#error "SPI3 not present in the selected device" +#endif + +#if !STM32_I2S_USE_SPI1 && !STM32_I2S_USE_SPI2 && !STM32_I2S_USE_SPI3 +#error "I2S driver activated but no SPI peripheral assigned" +#endif + +#if STM32_I2S_USE_SPI1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI1" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI2" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI3" +#endif + +#if STM32_I2S_USE_SPI1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI1" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI2" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI3" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_I2S_USE_SPI1 && (!defined(STM32_I2S_SPI1_RX_DMA_STREAM) || \ + !defined(STM32_I2S_SPI1_TX_DMA_STREAM)) +#error "SPI1 DMA streams not defined" +#endif + +#if STM32_I2S_USE_SPI2 && (!defined(STM32_I2S_SPI2_RX_DMA_STREAM) || \ + !defined(STM32_I2S_SPI2_TX_DMA_STREAM)) +#error "SPI2 DMA streams not defined" +#endif + +#if STM32_I2S_USE_SPI3 && (!defined(STM32_I2S_SPI3_RX_DMA_STREAM) || \ + !defined(STM32_I2S_SPI3_TX_DMA_STREAM)) +#error "SPI3 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_I2S_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 RX" +#endif + +#if STM32_I2S_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 TX" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 RX" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 TX" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 RX" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an I2S driver. + */ +typedef struct I2SDriver I2SDriver; + +/** + * @brief I2S notification callback type. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * @param[in] offset offset in buffers of the data to read/write + * @param[in] n number of samples to read/write + */ +typedef void (*i2scallback_t)(I2SDriver *i2sp, size_t offset, size_t n); + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Transmission buffer pointer. + * @note Can be @p NULL if TX is not required. + */ + const void *tx_buffer; + /** + * @brief Receive buffer pointer. + * @note Can be @p NULL if RX is not required. + */ + void *rx_buffer; + /** + * @brief TX and RX buffers size as number of samples. + */ + size_t size; + /** + * @brief Callback function called during streaming. + */ + i2scallback_t end_cb; + /* End of the mandatory fields.*/ + /** + * @brief Configuration of the I2SCFGR register. + * @details See the STM32 reference manual, this register is used for + * the I2S configuration, the following bits must not be + * specified because handled directly by the driver: + * - I2SMOD + * - I2SE + * - I2SCFG + * . + */ + int16_t i2scfgr; + /** + * @brief Configuration of the I2SPR register. + * @details See the STM32 reference manual, this register is used for + * the I2S clock setup. + */ + int16_t i2spr; +} I2SConfig; + +/** + * @brief Structure representing an I2S driver. + */ +struct I2SDriver { + /** + * @brief Driver state. + */ + i2sstate_t state; + /** + * @brief Current configuration data. + */ + const I2SConfig *config; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the SPIx registers block. + */ + SPI_TypeDef *spi; + /** + * @brief Calculated part of the I2SCFGR register. + */ + uint16_t cfg; + /** + * @brief Receive DMA stream or @p NULL. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA stream or @p NULL. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_I2S_USE_SPI1 && !defined(__DOXYGEN__) +extern I2SDriver I2SD1; +#endif + +#if STM32_I2S_USE_SPI2 && !defined(__DOXYGEN__) +extern I2SDriver I2SD2; +#endif + +#if STM32_I2S_USE_SPI3 && !defined(__DOXYGEN__) +extern I2SDriver I2SD3; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void i2s_lld_init(void); + void i2s_lld_start(I2SDriver *i2sp); + void i2s_lld_stop(I2SDriver *i2sp); + void i2s_lld_start_exchange(I2SDriver *i2sp); + void i2s_lld_stop_exchange(I2SDriver *i2sp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2S */ + +#endif /* _I2S_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.c b/os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.c new file mode 100644 index 000000000..b21faf364 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.c @@ -0,0 +1,633 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SPIv1/spi_lld.c + * @brief STM32 SPI subsystem low level driver source. + * + * @addtogroup SPI + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define SPI1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_RX_DMA_STREAM, \ + STM32_SPI1_RX_DMA_CHN) + +#define SPI1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_TX_DMA_STREAM, \ + STM32_SPI1_TX_DMA_CHN) + +#define SPI2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_RX_DMA_STREAM, \ + STM32_SPI2_RX_DMA_CHN) + +#define SPI2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_TX_DMA_STREAM, \ + STM32_SPI2_TX_DMA_CHN) + +#define SPI3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_RX_DMA_STREAM, \ + STM32_SPI3_RX_DMA_CHN) + +#define SPI3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_TX_DMA_STREAM, \ + STM32_SPI3_TX_DMA_CHN) + +#define SPI4_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_RX_DMA_STREAM, \ + STM32_SPI4_RX_DMA_CHN) + +#define SPI4_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_TX_DMA_STREAM, \ + STM32_SPI4_TX_DMA_CHN) + +#define SPI5_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_RX_DMA_STREAM, \ + STM32_SPI5_RX_DMA_CHN) + +#define SPI5_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_TX_DMA_STREAM, \ + STM32_SPI5_TX_DMA_CHN) + +#define SPI6_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_RX_DMA_STREAM, \ + STM32_SPI6_RX_DMA_CHN) + +#define SPI6_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_TX_DMA_STREAM, \ + STM32_SPI6_TX_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief SPI1 driver identifier.*/ +#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__) +SPIDriver SPID1; +#endif + +/** @brief SPI2 driver identifier.*/ +#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__) +SPIDriver SPID2; +#endif + +/** @brief SPI3 driver identifier.*/ +#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__) +SPIDriver SPID3; +#endif + +/** @brief SPI4 driver identifier.*/ +#if STM32_SPI_USE_SPI4 || defined(__DOXYGEN__) +SPIDriver SPID4; +#endif + +/** @brief SPI5 driver identifier.*/ +#if STM32_SPI_USE_SPI5 || defined(__DOXYGEN__) +SPIDriver SPID5; +#endif + +/** @brief SPI6 driver identifier.*/ +#if STM32_SPI_USE_SPI6 || defined(__DOXYGEN__) +SPIDriver SPID6; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const uint16_t dummytx = 0xFFFFU; +static uint16_t dummyrx; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Shared end-of-rx service routine. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_SPI_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_SPI_DMA_ERROR_HOOK(spip); + } +#else + (void)flags; +#endif + + /* Stop everything.*/ + dmaStreamDisable(spip->dmatx); + dmaStreamDisable(spip->dmarx); + + /* Portable SPI ISR code defined in the high level driver, note, it is + a macro.*/ + _spi_isr_code(spip); +} + +/** + * @brief Shared end-of-tx service routine. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_SPI_DMA_ERROR_HOOK) + (void)spip; + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_SPI_DMA_ERROR_HOOK(spip); + } +#else + (void)spip; + (void)flags; +#endif +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SPI driver initialization. + * + * @notapi + */ +void spi_lld_init(void) { + +#if STM32_SPI_USE_SPI1 + spiObjectInit(&SPID1); + SPID1.spi = SPI1; + SPID1.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM); + SPID1.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM); + SPID1.rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID1.txdmamode = STM32_DMA_CR_CHSEL(SPI1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI2 + spiObjectInit(&SPID2); + SPID2.spi = SPI2; + SPID2.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM); + SPID2.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM); + SPID2.rxdmamode = STM32_DMA_CR_CHSEL(SPI2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID2.txdmamode = STM32_DMA_CR_CHSEL(SPI2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI3 + spiObjectInit(&SPID3); + SPID3.spi = SPI3; + SPID3.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM); + SPID3.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM); + SPID3.rxdmamode = STM32_DMA_CR_CHSEL(SPI3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID3.txdmamode = STM32_DMA_CR_CHSEL(SPI3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI4 + spiObjectInit(&SPID4); + SPID4.spi = SPI4; + SPID4.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI4_RX_DMA_STREAM); + SPID4.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI4_TX_DMA_STREAM); + SPID4.rxdmamode = STM32_DMA_CR_CHSEL(SPI4_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID4.txdmamode = STM32_DMA_CR_CHSEL(SPI4_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI5 + spiObjectInit(&SPID5); + SPID5.spi = SPI5; + SPID5.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI5_RX_DMA_STREAM); + SPID5.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI5_TX_DMA_STREAM); + SPID5.rxdmamode = STM32_DMA_CR_CHSEL(SPI5_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID5.txdmamode = STM32_DMA_CR_CHSEL(SPI5_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI6 + spiObjectInit(&SPID6); + SPID6.spi = SPI6; + SPID6.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI6_RX_DMA_STREAM); + SPID6.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI6_TX_DMA_STREAM); + SPID6.rxdmamode = STM32_DMA_CR_CHSEL(SPI6_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID6.txdmamode = STM32_DMA_CR_CHSEL(SPI6_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif +} + +/** + * @brief Configures and activates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_start(SPIDriver *spip) { + + /* If in stopped state then enables the SPI and DMA clocks.*/ + if (spip->state == SPI_STOP) { +#if STM32_SPI_USE_SPI1 + if (&SPID1 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI1(FALSE); + } +#endif +#if STM32_SPI_USE_SPI2 + if (&SPID2 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI2(FALSE); + } +#endif +#if STM32_SPI_USE_SPI3 + if (&SPID3 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI3(FALSE); + } +#endif +#if STM32_SPI_USE_SPI4 + if (&SPID4 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI4_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI4_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI4(FALSE); + } +#endif +#if STM32_SPI_USE_SPI5 + if (&SPID5 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI5_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI5_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI5(FALSE); + } +#endif +#if STM32_SPI_USE_SPI6 + if (&SPID6 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI6_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI6_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI6(FALSE); + } +#endif + + /* DMA setup.*/ + dmaStreamSetPeripheral(spip->dmarx, &spip->spi->DR); + dmaStreamSetPeripheral(spip->dmatx, &spip->spi->DR); + } + + /* Configuration-specific DMA setup.*/ + if ((spip->config->cr1 & SPI_CR1_DFF) == 0) { + /* Frame width is 8 bits or smaller.*/ + spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; + spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; + } + else { + /* Frame width is larger than 8 bits.*/ + spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + } + /* SPI setup and enable.*/ + spip->spi->CR1 = 0; + spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR | SPI_CR1_SSM | + SPI_CR1_SSI; + spip->spi->CR2 = SPI_CR2_SSOE | SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; + spip->spi->CR1 |= SPI_CR1_SPE; +} + +/** + * @brief Deactivates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_stop(SPIDriver *spip) { + + /* If in ready state then disables the SPI clock.*/ + if (spip->state == SPI_READY) { + + /* SPI disable.*/ + spip->spi->CR1 = 0; + spip->spi->CR2 = 0; + dmaStreamRelease(spip->dmarx); + dmaStreamRelease(spip->dmatx); + +#if STM32_SPI_USE_SPI1 + if (&SPID1 == spip) + rccDisableSPI1(FALSE); +#endif +#if STM32_SPI_USE_SPI2 + if (&SPID2 == spip) + rccDisableSPI2(FALSE); +#endif +#if STM32_SPI_USE_SPI3 + if (&SPID3 == spip) + rccDisableSPI3(FALSE); +#endif +#if STM32_SPI_USE_SPI4 + if (&SPID4 == spip) + rccDisableSPI4(FALSE); +#endif +#if STM32_SPI_USE_SPI5 + if (&SPID5 == spip) + rccDisableSPI5(FALSE); +#endif +#if STM32_SPI_USE_SPI6 + if (&SPID6 == spip) + rccDisableSPI6(FALSE); +#endif + } +} + +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_select(SPIDriver *spip) { + + palClearPad(spip->config->ssport, spip->config->sspad); +} + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_unselect(SPIDriver *spip) { + + palSetPad(spip->config->ssport, spip->config->sspad); +} + +/** + * @brief Ignores data on the SPI bus. + * @details This asynchronous function starts the transmission of a series of + * idle words on the SPI bus and ignores the received data. + * @post At the end of the operation the configured callback is invoked. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * + * @notapi + */ +void spi_lld_ignore(SPIDriver *spip, size_t n) { + + dmaStreamSetMemory0(spip->dmarx, &dummyrx); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode); + + dmaStreamSetMemory0(spip->dmatx, &dummytx); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This asynchronous function starts a simultaneous transmit/receive + * operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + + dmaStreamSetMemory0(spip->dmarx, rxbuf); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode| STM32_DMA_CR_MINC); + + dmaStreamSetMemory0(spip->dmatx, txbuf); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Sends data over the SPI bus. + * @details This asynchronous function starts a transmit operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @notapi + */ +void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { + + dmaStreamSetMemory0(spip->dmarx, &dummyrx); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode); + + dmaStreamSetMemory0(spip->dmatx, txbuf); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Receives data from the SPI bus. + * @details This asynchronous function starts a receive operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { + + dmaStreamSetMemory0(spip->dmarx, rxbuf); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC); + + dmaStreamSetMemory0(spip->dmatx, &dummytx); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Exchanges one frame using a polled wait. + * @details This synchronous function exchanges one frame using a polled + * synchronization method. This function is useful when exchanging + * small amount of data on high speed channels, usually in this + * situation is much more efficient just wait for completion using + * polling than suspending the thread waiting for an interrupt. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] frame the data frame to send over the SPI bus + * @return The received data frame from the SPI bus. + */ +uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { + + spip->spi->DR = frame; + while ((spip->spi->SR & SPI_SR_RXNE) == 0) + ; + return spip->spi->DR; +} + +#endif /* HAL_USE_SPI */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.h b/os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.h new file mode 100644 index 000000000..b06ab86ad --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv1/hal_spi_lld.h @@ -0,0 +1,543 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SPIv1/spi_lld.h + * @brief STM32 SPI subsystem low level driver header. + * + * @addtogroup SPI + * @{ + */ + +#ifndef _SPI_LLD_H_ +#define _SPI_LLD_H_ + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SPI1 driver enable switch. + * @details If set to @p TRUE the support for SPI1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI1) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI1 FALSE +#endif + +/** + * @brief SPI2 driver enable switch. + * @details If set to @p TRUE the support for SPI2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI2) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI2 FALSE +#endif + +/** + * @brief SPI3 driver enable switch. + * @details If set to @p TRUE the support for SPI3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI3) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI3 FALSE +#endif + +/** + * @brief SPI4 driver enable switch. + * @details If set to @p TRUE the support for SPI4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI4) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI4 FALSE +#endif + +/** + * @brief SPI5 driver enable switch. + * @details If set to @p TRUE the support for SPI5 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI5) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI5 FALSE +#endif + +/** + * @brief SPI6 driver enable switch. + * @details If set to @p TRUE the support for SPI6 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI6) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI6 FALSE +#endif + +/** + * @brief SPI1 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI1_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI2 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI2_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI3 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI3_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI4 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI4_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI5 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI5_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI6 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI6_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI6_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI1 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI1_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI2 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI2_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI3 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI3_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI4 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI4_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI5 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI5_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI5_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI6 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI6_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI6_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI DMA error hook. + */ +#if !defined(STM32_SPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1 +#error "SPI1 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI2 && !STM32_HAS_SPI2 +#error "SPI2 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI3 && !STM32_HAS_SPI3 +#error "SPI3 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI4 && !STM32_HAS_SPI4 +#error "SPI4 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI5 && !STM32_HAS_SPI5 +#error "SPI5 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI6 && !STM32_HAS_SPI6 +#error "SPI6 not present in the selected device" +#endif + +#if !STM32_SPI_USE_SPI1 && !STM32_SPI_USE_SPI2 && !STM32_SPI_USE_SPI3 && \ + !STM32_SPI_USE_SPI4 && !STM32_SPI_USE_SPI5 && !STM32_SPI_USE_SPI6 +#error "SPI driver activated but no SPI peripheral assigned" +#endif + +#if STM32_SPI_USE_SPI1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI1" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI2" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI3" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI4" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI5" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI6_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI6" +#endif + +#if STM32_SPI_USE_SPI1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI1" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI2" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI3" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI4" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI5_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI5" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI6_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI6" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_SPI_USE_SPI1 && (!defined(STM32_SPI_SPI1_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI1_TX_DMA_STREAM)) +#error "SPI1 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI2 && (!defined(STM32_SPI_SPI2_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI2_TX_DMA_STREAM)) +#error "SPI2 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI3 && (!defined(STM32_SPI_SPI3_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI3_TX_DMA_STREAM)) +#error "SPI3 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI4 && (!defined(STM32_SPI_SPI4_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI4_TX_DMA_STREAM)) +#error "SPI4 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI5 && (!defined(STM32_SPI_SPI5_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI5_TX_DMA_STREAM)) +#error "SPI5 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI6 && (!defined(STM32_SPI_SPI6_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI6_TX_DMA_STREAM)) +#error "SPI6 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_SPI_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 RX" +#endif + +#if STM32_SPI_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 TX" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 RX" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 TX" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 RX" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 TX" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_RX_DMA_STREAM, STM32_SPI4_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI4 RX" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_TX_DMA_STREAM, STM32_SPI4_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI4 TX" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_RX_DMA_STREAM, STM32_SPI5_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI5 RX" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_TX_DMA_STREAM, STM32_SPI5_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI5 TX" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_RX_DMA_STREAM, STM32_SPI6_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI6 RX" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_TX_DMA_STREAM, STM32_SPI6_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI6 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an SPI driver. + */ +typedef struct SPIDriver SPIDriver; + +/** + * @brief SPI notification callback type. + * + * @param[in] spip pointer to the @p SPIDriver object triggering the + * callback + */ +typedef void (*spicallback_t)(SPIDriver *spip); + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief Operation complete callback or @p NULL. + */ + spicallback_t end_cb; + /* End of the mandatory fields.*/ + /** + * @brief The chip select line port. + */ + ioportid_t ssport; + /** + * @brief The chip select line pad number. + */ + uint16_t sspad; + /** + * @brief SPI initialization data. + */ + uint16_t cr1; +} SPIConfig; + +/** + * @brief Structure representing an SPI driver. + */ +struct SPIDriver { + /** + * @brief Driver state. + */ + spistate_t state; + /** + * @brief Current configuration data. + */ + const SPIConfig *config; +#if SPI_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* SPI_USE_WAIT */ +#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the bus. + */ + mutex_t mutex; +#endif /* SPI_USE_MUTUAL_EXCLUSION */ +#if defined(SPI_DRIVER_EXT_FIELDS) + SPI_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the SPIx registers block. + */ + SPI_TypeDef *spi; + /** + * @brief Receive DMA stream. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA stream. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__) +extern SPIDriver SPID1; +#endif + +#if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__) +extern SPIDriver SPID2; +#endif + +#if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__) +extern SPIDriver SPID3; +#endif + +#if STM32_SPI_USE_SPI4 && !defined(__DOXYGEN__) +extern SPIDriver SPID4; +#endif + +#if STM32_SPI_USE_SPI5 && !defined(__DOXYGEN__) +extern SPIDriver SPID5; +#endif + +#if STM32_SPI_USE_SPI6 && !defined(__DOXYGEN__) +extern SPIDriver SPID6; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void spi_lld_init(void); + void spi_lld_start(SPIDriver *spip); + void spi_lld_stop(SPIDriver *spip); + void spi_lld_select(SPIDriver *spip); + void spi_lld_unselect(SPIDriver *spip); + void spi_lld_ignore(SPIDriver *spip, size_t n); + void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); + void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); + uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SPI */ + +#endif /* _SPI_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/i2s_lld.c b/os/hal/ports/STM32/LLD/SPIv1/i2s_lld.c deleted file mode 100644 index e1f75c50f..000000000 --- a/os/hal/ports/STM32/LLD/SPIv1/i2s_lld.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file i2s_lld.c - * @brief STM32 I2S subsystem low level driver source. - * - * @addtogroup I2S - * @{ - */ - -#include "hal.h" - -#if HAL_USE_I2S || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define I2S1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_RX_DMA_STREAM, \ - STM32_SPI1_RX_DMA_CHN) - -#define I2S1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_TX_DMA_STREAM, \ - STM32_SPI1_TX_DMA_CHN) - -#define I2S2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_RX_DMA_STREAM, \ - STM32_SPI2_RX_DMA_CHN) - -#define I2S2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_TX_DMA_STREAM, \ - STM32_SPI2_TX_DMA_CHN) - -#define I2S3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_RX_DMA_STREAM, \ - STM32_SPI3_RX_DMA_CHN) - -#define I2S3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_TX_DMA_STREAM, \ - STM32_SPI3_TX_DMA_CHN) - -/* - * Static I2S settings for I2S1. - */ -#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG 0 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 -#endif -#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ - SPI_I2SCFGR_I2SCFG_0) -#endif -#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ - -/* - * Static I2S settings for I2S2. - */ -#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG 0 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 -#endif -#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ - SPI_I2SCFGR_I2SCFG_0) -#endif -#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ - -/* - * Static I2S settings for I2S3. - */ -#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG 0 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 -#endif -#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ - SPI_I2SCFGR_I2SCFG_0) -#endif -#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief I2S1 driver identifier.*/ -#if STM32_I2S_USE_SPI1 || defined(__DOXYGEN__) -I2SDriver I2SD1; -#endif - -/** @brief I2S2 driver identifier.*/ -#if STM32_I2S_USE_SPI2 || defined(__DOXYGEN__) -I2SDriver I2SD2; -#endif - -/** @brief I2S3 driver identifier.*/ -#if STM32_I2S_USE_SPI3 || defined(__DOXYGEN__) -I2SDriver I2SD3; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) || \ - STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) || \ - STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) -/** - * @brief Shared end-of-rx service routine. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void i2s_lld_serve_rx_interrupt(I2SDriver *i2sp, uint32_t flags) { - - (void)i2sp; - - /* DMA errors handling.*/ -#if defined(STM32_I2S_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2S_DMA_ERROR_HOOK(i2sp); - } -#endif - - /* Callbacks handling, note it is portable code defined in the high - level driver.*/ - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _i2s_isr_full_code(i2sp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _i2s_isr_half_code(i2sp); - } -} -#endif - -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) || \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) || \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) -/** - * @brief Shared end-of-tx service routine. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void i2s_lld_serve_tx_interrupt(I2SDriver *i2sp, uint32_t flags) { - - (void)i2sp; - - /* DMA errors handling.*/ -#if defined(STM32_I2S_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2S_DMA_ERROR_HOOK(i2sp); - } -#endif - - /* Callbacks handling, note it is portable code defined in the high - level driver.*/ - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _i2s_isr_full_code(i2sp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _i2s_isr_half_code(i2sp); - } -} -#endif - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2S driver initialization. - * - * @notapi - */ -void i2s_lld_init(void) { - -#if STM32_I2S_USE_SPI1 - i2sObjectInit(&I2SD1); - I2SD1.spi = SPI1; - I2SD1.cfg = STM32_I2S1_CFGR_CFG; -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) - I2SD1.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI1_RX_DMA_STREAM); - I2SD1.rxdmamode = STM32_DMA_CR_CHSEL(I2S1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD1.dmarx = NULL; - I2SD1.rxdmamode = 0; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) - I2SD1.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI1_TX_DMA_STREAM); - I2SD1.txdmamode = STM32_DMA_CR_CHSEL(I2S1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD1.dmatx = NULL; - I2SD1.txdmamode = 0; -#endif -#endif - -#if STM32_I2S_USE_SPI2 - i2sObjectInit(&I2SD2); - I2SD2.spi = SPI2; - I2SD2.cfg = STM32_I2S2_CFGR_CFG; -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) - I2SD2.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI2_RX_DMA_STREAM); - I2SD2.rxdmamode = STM32_DMA_CR_CHSEL(I2S2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD2.dmarx = NULL; - I2SD2.rxdmamode = 0; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) - I2SD2.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI2_TX_DMA_STREAM); - I2SD2.txdmamode = STM32_DMA_CR_CHSEL(I2S2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD2.dmatx = NULL; - I2SD2.txdmamode = 0; -#endif -#endif - -#if STM32_I2S_USE_SPI3 - i2sObjectInit(&I2SD3); - I2SD3.spi = SPI3; - I2SD3.cfg = STM32_I2S3_CFGR_CFG; -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) - I2SD3.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI3_RX_DMA_STREAM); - I2SD3.rxdmamode = STM32_DMA_CR_CHSEL(I2S3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD3.dmarx = NULL; - I2SD3.rxdmamode = 0; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) - I2SD3.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI3_TX_DMA_STREAM); - I2SD3.txdmamode = STM32_DMA_CR_CHSEL(I2S3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD3.dmatx = NULL; - I2SD3.txdmamode = 0; -#endif -#endif -} - -/** - * @brief Configures and activates the I2S peripheral. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_start(I2SDriver *i2sp) { - - /* If in stopped state then enables the SPI and DMA clocks.*/ - if (i2sp->state == I2S_STOP) { - -#if STM32_I2S_USE_SPI1 - if (&I2SD1 == i2sp) { - bool b; - - /* Enabling I2S unit clock.*/ - rccEnableSPI1(FALSE); - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) - b = dmaStreamAllocate(i2sp->dmarx, - STM32_I2S_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_RXDMAEN; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) - b = dmaStreamAllocate(i2sp->dmatx, - STM32_I2S_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_TXDMAEN; -#endif - } -#endif - -#if STM32_I2S_USE_SPI2 - if (&I2SD2 == i2sp) { - bool b; - - /* Enabling I2S unit clock.*/ - rccEnableSPI2(FALSE); - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) - b = dmaStreamAllocate(i2sp->dmarx, - STM32_I2S_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_RXDMAEN; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) - b = dmaStreamAllocate(i2sp->dmatx, - STM32_I2S_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_TXDMAEN; -#endif - } -#endif - -#if STM32_I2S_USE_SPI3 - if (&I2SD3 == i2sp) { - bool b; - - /* Enabling I2S unit clock.*/ - rccEnableSPI3(FALSE); - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) - b = dmaStreamAllocate(i2sp->dmarx, - STM32_I2S_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_RXDMAEN; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) - b = dmaStreamAllocate(i2sp->dmatx, - STM32_I2S_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_TXDMAEN; -#endif - } -#endif - } - - /* I2S (re)configuration.*/ - i2sp->spi->I2SPR = i2sp->config->i2spr; - i2sp->spi->I2SCFGR = i2sp->config->i2scfgr | i2sp->cfg | SPI_I2SCFGR_I2SMOD; -} - -/** - * @brief Deactivates the I2S peripheral. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_stop(I2SDriver *i2sp) { - - /* If in ready state then disables the SPI clock.*/ - if (i2sp->state == I2S_READY) { - - /* SPI disable.*/ - i2sp->spi->CR2 = 0; - if (NULL != i2sp->dmarx) - dmaStreamRelease(i2sp->dmarx); - if (NULL != i2sp->dmatx) - dmaStreamRelease(i2sp->dmatx); - -#if STM32_I2S_USE_SPI1 - if (&I2SD1 == i2sp) - rccDisableSPI1(FALSE); -#endif - -#if STM32_I2S_USE_SPI2 - if (&I2SD2 == i2sp) - rccDisableSPI2(FALSE); -#endif - -#if STM32_I2S_USE_SPI3 - if (&I2SD3 == i2sp) - rccDisableSPI3(FALSE); -#endif - } -} - -/** - * @brief Starts a I2S data exchange. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_start_exchange(I2SDriver *i2sp) { - size_t size = i2sp->config->size; - - /* In 32 bit modes the DMA has to perform double operations because fetches - are always performed using 16 bit accesses. - DATLEN CHLEN SIZE - 00 (16) 0 (16) 16 - 00 (16) 1 (32) 16 - 01 (24) X 32 - 10 (32) X 32 - 11 (NA) X NA - */ - if ((i2sp->config->i2scfgr & SPI_I2SCFGR_DATLEN) != 0) - size *= 2; - - /* RX DMA setup.*/ - if (NULL != i2sp->dmarx) { - dmaStreamSetMode(i2sp->dmarx, i2sp->rxdmamode); - dmaStreamSetPeripheral(i2sp->dmarx, &i2sp->spi->DR); - dmaStreamSetMemory0(i2sp->dmarx, i2sp->config->rx_buffer); - dmaStreamSetTransactionSize(i2sp->dmarx, size); - dmaStreamEnable(i2sp->dmarx); - } - - /* TX DMA setup.*/ - if (NULL != i2sp->dmatx) { - dmaStreamSetMode(i2sp->dmatx, i2sp->txdmamode); - dmaStreamSetPeripheral(i2sp->dmatx, &i2sp->spi->DR); - dmaStreamSetMemory0(i2sp->dmatx, i2sp->config->tx_buffer); - dmaStreamSetTransactionSize(i2sp->dmatx, size); - dmaStreamEnable(i2sp->dmatx); - } - - /* Starting transfer.*/ - i2sp->spi->I2SCFGR |= SPI_I2SCFGR_I2SE; -} - -/** - * @brief Stops the ongoing data exchange. - * @details The ongoing data exchange, if any, is stopped, if the driver - * was not active the function does nothing. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_stop_exchange(I2SDriver *i2sp) { - - /* Stop TX DMA, if enabled.*/ - if (NULL != i2sp->dmatx) { - dmaStreamDisable(i2sp->dmatx); - - /* From the RM: To switch off the I2S, by clearing I2SE, it is mandatory - to wait for TXE = 1 and BSY = 0.*/ - while ((i2sp->spi->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE) - ; - } - - /* Stop SPI/I2S peripheral.*/ - i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE; - - /* Stop RX DMA, if enabled.*/ - if (NULL != i2sp->dmarx) - dmaStreamDisable(i2sp->dmarx); -} - -#endif /* HAL_USE_I2S */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/i2s_lld.h b/os/hal/ports/STM32/LLD/SPIv1/i2s_lld.h deleted file mode 100644 index 0941ae3a8..000000000 --- a/os/hal/ports/STM32/LLD/SPIv1/i2s_lld.h +++ /dev/null @@ -1,432 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file i2s_lld.h - * @brief STM32 I2S subsystem low level driver header. - * - * @addtogroup I2S - * @{ - */ - -#ifndef _I2S_LLD_H_ -#define _I2S_LLD_H_ - -#if HAL_USE_I2S || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Static I2S modes - * @{ - */ -#define STM32_I2S_MODE_SLAVE 0 -#define STM32_I2S_MODE_MASTER 1 -#define STM32_I2S_MODE_RX 2 -#define STM32_I2S_MODE_TX 4 -#define STM32_I2S_MODE_RXTX (STM32_I2S_MODE_RX | \ - STM32_I2S_MODE_TX) -/** @} */ - -/** - * @name Mode checks - * @{ - */ -#define STM32_I2S_IS_MASTER(mode) ((mode) & STM32_I2S_MODE_MASTER) -#define STM32_I2S_RX_ENABLED(mode) ((mode) & STM32_I2S_MODE_RX) -#define STM32_I2S_TX_ENABLED(mode) ((mode) & STM32_I2S_MODE_TX) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2S1 driver enable switch. - * @details If set to @p TRUE the support for I2S1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_I2S_USE_SPI1) || defined(__DOXYGEN__) -#define STM32_I2S_USE_SPI1 FALSE -#endif - -/** - * @brief I2S2 driver enable switch. - * @details If set to @p TRUE the support for I2S2 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_I2S_USE_SPI2) || defined(__DOXYGEN__) -#define STM32_I2S_USE_SPI2 FALSE -#endif - -/** - * @brief I2S3 driver enable switch. - * @details If set to @p TRUE the support for I2S3 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_I2S_USE_SPI3) || defined(__DOXYGEN__) -#define STM32_I2S_USE_SPI3 FALSE -#endif - -/** - * @brief I2S1 mode. - */ -#if !defined(STM32_I2S_SPI1_MODE) || defined(__DOXYGEN__) -#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \ - STM32_I2S_MODE_RX) -#endif - -/** - * @brief I2S2 mode. - */ -#if !defined(STM32_I2S_SPI2_MODE) || defined(__DOXYGEN__) -#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \ - STM32_I2S_MODE_RX) -#endif - -/** - * @brief I2S3 mode. - */ -#if !defined(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) -#define STM32_I2S_SPI3_MODE (STM32_I2S_MODE_MASTER | \ - STM32_I2S_MODE_RX) -#endif - -/** - * @brief I2S1 interrupt priority level setting. - */ -#if !defined(STM32_I2S_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI1_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2S2 interrupt priority level setting. - */ -#if !defined(STM32_I2S_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI2_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2S3 interrupt priority level setting. - */ -#if !defined(STM32_I2S_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI3_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2S1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_I2S_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI1_DMA_PRIORITY 1 -#endif - -/** - * @brief I2S2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_I2S_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI2_DMA_PRIORITY 1 -#endif - -/** - * @brief I2S3 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_I2S_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI3_DMA_PRIORITY 1 -#endif - -/** - * @brief I2S DMA error hook. - */ -#if !defined(STM32_I2S_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_I2S_USE_SPI1 && !STM32_SPI1_SUPPORTS_I2S -#error "SPI1 does not support I2S mode" -#endif - -#if STM32_I2S_USE_SPI2 && !STM32_SPI2_SUPPORTS_I2S -#error "SPI2 does not support I2S mode" -#endif - -#if STM32_I2S_USE_SPI3 && !STM32_SPI3_SUPPORTS_I2S -#error "SPI3 does not support I2S mode" -#endif - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) && \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) -#error "I2S1 RX and TX mode not supported in this driver implementation" -#endif - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) && \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) -#error "I2S2 RX and TX mode not supported in this driver implementation" -#endif - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) && \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) -#error "I2S3 RX and TX mode not supported in this driver implementation" -#endif - -#if STM32_I2S_USE_SPI1 && !STM32_HAS_SPI1 -#error "SPI1 not present in the selected device" -#endif - -#if STM32_I2S_USE_SPI2 && !STM32_HAS_SPI2 -#error "SPI2 not present in the selected device" -#endif - -#if STM32_I2S_USE_SPI3 && !STM32_HAS_SPI3 -#error "SPI3 not present in the selected device" -#endif - -#if !STM32_I2S_USE_SPI1 && !STM32_I2S_USE_SPI2 && !STM32_I2S_USE_SPI3 -#error "I2S driver activated but no SPI peripheral assigned" -#endif - -#if STM32_I2S_USE_SPI1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI1" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI2" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI3" -#endif - -#if STM32_I2S_USE_SPI1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI1" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI2" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI3" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_I2S_USE_SPI1 && (!defined(STM32_I2S_SPI1_RX_DMA_STREAM) || \ - !defined(STM32_I2S_SPI1_TX_DMA_STREAM)) -#error "SPI1 DMA streams not defined" -#endif - -#if STM32_I2S_USE_SPI2 && (!defined(STM32_I2S_SPI2_RX_DMA_STREAM) || \ - !defined(STM32_I2S_SPI2_TX_DMA_STREAM)) -#error "SPI2 DMA streams not defined" -#endif - -#if STM32_I2S_USE_SPI3 && (!defined(STM32_I2S_SPI3_RX_DMA_STREAM) || \ - !defined(STM32_I2S_SPI3_TX_DMA_STREAM)) -#error "SPI3 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_I2S_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 RX" -#endif - -#if STM32_I2S_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 TX" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 RX" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 TX" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 RX" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an I2S driver. - */ -typedef struct I2SDriver I2SDriver; - -/** - * @brief I2S notification callback type. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * @param[in] offset offset in buffers of the data to read/write - * @param[in] n number of samples to read/write - */ -typedef void (*i2scallback_t)(I2SDriver *i2sp, size_t offset, size_t n); - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Transmission buffer pointer. - * @note Can be @p NULL if TX is not required. - */ - const void *tx_buffer; - /** - * @brief Receive buffer pointer. - * @note Can be @p NULL if RX is not required. - */ - void *rx_buffer; - /** - * @brief TX and RX buffers size as number of samples. - */ - size_t size; - /** - * @brief Callback function called during streaming. - */ - i2scallback_t end_cb; - /* End of the mandatory fields.*/ - /** - * @brief Configuration of the I2SCFGR register. - * @details See the STM32 reference manual, this register is used for - * the I2S configuration, the following bits must not be - * specified because handled directly by the driver: - * - I2SMOD - * - I2SE - * - I2SCFG - * . - */ - int16_t i2scfgr; - /** - * @brief Configuration of the I2SPR register. - * @details See the STM32 reference manual, this register is used for - * the I2S clock setup. - */ - int16_t i2spr; -} I2SConfig; - -/** - * @brief Structure representing an I2S driver. - */ -struct I2SDriver { - /** - * @brief Driver state. - */ - i2sstate_t state; - /** - * @brief Current configuration data. - */ - const I2SConfig *config; - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the SPIx registers block. - */ - SPI_TypeDef *spi; - /** - * @brief Calculated part of the I2SCFGR register. - */ - uint16_t cfg; - /** - * @brief Receive DMA stream or @p NULL. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA stream or @p NULL. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_I2S_USE_SPI1 && !defined(__DOXYGEN__) -extern I2SDriver I2SD1; -#endif - -#if STM32_I2S_USE_SPI2 && !defined(__DOXYGEN__) -extern I2SDriver I2SD2; -#endif - -#if STM32_I2S_USE_SPI3 && !defined(__DOXYGEN__) -extern I2SDriver I2SD3; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void i2s_lld_init(void); - void i2s_lld_start(I2SDriver *i2sp); - void i2s_lld_stop(I2SDriver *i2sp); - void i2s_lld_start_exchange(I2SDriver *i2sp); - void i2s_lld_stop_exchange(I2SDriver *i2sp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2S */ - -#endif /* _I2S_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/spi_lld.c b/os/hal/ports/STM32/LLD/SPIv1/spi_lld.c deleted file mode 100644 index b21faf364..000000000 --- a/os/hal/ports/STM32/LLD/SPIv1/spi_lld.c +++ /dev/null @@ -1,633 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SPIv1/spi_lld.c - * @brief STM32 SPI subsystem low level driver source. - * - * @addtogroup SPI - * @{ - */ - -#include "hal.h" - -#if HAL_USE_SPI || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define SPI1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_RX_DMA_STREAM, \ - STM32_SPI1_RX_DMA_CHN) - -#define SPI1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_TX_DMA_STREAM, \ - STM32_SPI1_TX_DMA_CHN) - -#define SPI2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_RX_DMA_STREAM, \ - STM32_SPI2_RX_DMA_CHN) - -#define SPI2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_TX_DMA_STREAM, \ - STM32_SPI2_TX_DMA_CHN) - -#define SPI3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_RX_DMA_STREAM, \ - STM32_SPI3_RX_DMA_CHN) - -#define SPI3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_TX_DMA_STREAM, \ - STM32_SPI3_TX_DMA_CHN) - -#define SPI4_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_RX_DMA_STREAM, \ - STM32_SPI4_RX_DMA_CHN) - -#define SPI4_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_TX_DMA_STREAM, \ - STM32_SPI4_TX_DMA_CHN) - -#define SPI5_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_RX_DMA_STREAM, \ - STM32_SPI5_RX_DMA_CHN) - -#define SPI5_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_TX_DMA_STREAM, \ - STM32_SPI5_TX_DMA_CHN) - -#define SPI6_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_RX_DMA_STREAM, \ - STM32_SPI6_RX_DMA_CHN) - -#define SPI6_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_TX_DMA_STREAM, \ - STM32_SPI6_TX_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief SPI1 driver identifier.*/ -#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__) -SPIDriver SPID1; -#endif - -/** @brief SPI2 driver identifier.*/ -#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__) -SPIDriver SPID2; -#endif - -/** @brief SPI3 driver identifier.*/ -#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__) -SPIDriver SPID3; -#endif - -/** @brief SPI4 driver identifier.*/ -#if STM32_SPI_USE_SPI4 || defined(__DOXYGEN__) -SPIDriver SPID4; -#endif - -/** @brief SPI5 driver identifier.*/ -#if STM32_SPI_USE_SPI5 || defined(__DOXYGEN__) -SPIDriver SPID5; -#endif - -/** @brief SPI6 driver identifier.*/ -#if STM32_SPI_USE_SPI6 || defined(__DOXYGEN__) -SPIDriver SPID6; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -static const uint16_t dummytx = 0xFFFFU; -static uint16_t dummyrx; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Shared end-of-rx service routine. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_SPI_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_SPI_DMA_ERROR_HOOK(spip); - } -#else - (void)flags; -#endif - - /* Stop everything.*/ - dmaStreamDisable(spip->dmatx); - dmaStreamDisable(spip->dmarx); - - /* Portable SPI ISR code defined in the high level driver, note, it is - a macro.*/ - _spi_isr_code(spip); -} - -/** - * @brief Shared end-of-tx service routine. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_SPI_DMA_ERROR_HOOK) - (void)spip; - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_SPI_DMA_ERROR_HOOK(spip); - } -#else - (void)spip; - (void)flags; -#endif -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SPI driver initialization. - * - * @notapi - */ -void spi_lld_init(void) { - -#if STM32_SPI_USE_SPI1 - spiObjectInit(&SPID1); - SPID1.spi = SPI1; - SPID1.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM); - SPID1.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM); - SPID1.rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID1.txdmamode = STM32_DMA_CR_CHSEL(SPI1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI2 - spiObjectInit(&SPID2); - SPID2.spi = SPI2; - SPID2.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM); - SPID2.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM); - SPID2.rxdmamode = STM32_DMA_CR_CHSEL(SPI2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID2.txdmamode = STM32_DMA_CR_CHSEL(SPI2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI3 - spiObjectInit(&SPID3); - SPID3.spi = SPI3; - SPID3.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM); - SPID3.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM); - SPID3.rxdmamode = STM32_DMA_CR_CHSEL(SPI3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID3.txdmamode = STM32_DMA_CR_CHSEL(SPI3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI4 - spiObjectInit(&SPID4); - SPID4.spi = SPI4; - SPID4.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI4_RX_DMA_STREAM); - SPID4.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI4_TX_DMA_STREAM); - SPID4.rxdmamode = STM32_DMA_CR_CHSEL(SPI4_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID4.txdmamode = STM32_DMA_CR_CHSEL(SPI4_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI5 - spiObjectInit(&SPID5); - SPID5.spi = SPI5; - SPID5.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI5_RX_DMA_STREAM); - SPID5.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI5_TX_DMA_STREAM); - SPID5.rxdmamode = STM32_DMA_CR_CHSEL(SPI5_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID5.txdmamode = STM32_DMA_CR_CHSEL(SPI5_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI6 - spiObjectInit(&SPID6); - SPID6.spi = SPI6; - SPID6.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI6_RX_DMA_STREAM); - SPID6.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI6_TX_DMA_STREAM); - SPID6.rxdmamode = STM32_DMA_CR_CHSEL(SPI6_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID6.txdmamode = STM32_DMA_CR_CHSEL(SPI6_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif -} - -/** - * @brief Configures and activates the SPI peripheral. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_start(SPIDriver *spip) { - - /* If in stopped state then enables the SPI and DMA clocks.*/ - if (spip->state == SPI_STOP) { -#if STM32_SPI_USE_SPI1 - if (&SPID1 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI1(FALSE); - } -#endif -#if STM32_SPI_USE_SPI2 - if (&SPID2 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI2(FALSE); - } -#endif -#if STM32_SPI_USE_SPI3 - if (&SPID3 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI3(FALSE); - } -#endif -#if STM32_SPI_USE_SPI4 - if (&SPID4 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI4_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI4_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI4(FALSE); - } -#endif -#if STM32_SPI_USE_SPI5 - if (&SPID5 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI5_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI5_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI5(FALSE); - } -#endif -#if STM32_SPI_USE_SPI6 - if (&SPID6 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI6_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI6_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI6(FALSE); - } -#endif - - /* DMA setup.*/ - dmaStreamSetPeripheral(spip->dmarx, &spip->spi->DR); - dmaStreamSetPeripheral(spip->dmatx, &spip->spi->DR); - } - - /* Configuration-specific DMA setup.*/ - if ((spip->config->cr1 & SPI_CR1_DFF) == 0) { - /* Frame width is 8 bits or smaller.*/ - spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; - spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; - } - else { - /* Frame width is larger than 8 bits.*/ - spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - } - /* SPI setup and enable.*/ - spip->spi->CR1 = 0; - spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR | SPI_CR1_SSM | - SPI_CR1_SSI; - spip->spi->CR2 = SPI_CR2_SSOE | SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; - spip->spi->CR1 |= SPI_CR1_SPE; -} - -/** - * @brief Deactivates the SPI peripheral. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_stop(SPIDriver *spip) { - - /* If in ready state then disables the SPI clock.*/ - if (spip->state == SPI_READY) { - - /* SPI disable.*/ - spip->spi->CR1 = 0; - spip->spi->CR2 = 0; - dmaStreamRelease(spip->dmarx); - dmaStreamRelease(spip->dmatx); - -#if STM32_SPI_USE_SPI1 - if (&SPID1 == spip) - rccDisableSPI1(FALSE); -#endif -#if STM32_SPI_USE_SPI2 - if (&SPID2 == spip) - rccDisableSPI2(FALSE); -#endif -#if STM32_SPI_USE_SPI3 - if (&SPID3 == spip) - rccDisableSPI3(FALSE); -#endif -#if STM32_SPI_USE_SPI4 - if (&SPID4 == spip) - rccDisableSPI4(FALSE); -#endif -#if STM32_SPI_USE_SPI5 - if (&SPID5 == spip) - rccDisableSPI5(FALSE); -#endif -#if STM32_SPI_USE_SPI6 - if (&SPID6 == spip) - rccDisableSPI6(FALSE); -#endif - } -} - -/** - * @brief Asserts the slave select signal and prepares for transfers. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_select(SPIDriver *spip) { - - palClearPad(spip->config->ssport, spip->config->sspad); -} - -/** - * @brief Deasserts the slave select signal. - * @details The previously selected peripheral is unselected. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_unselect(SPIDriver *spip) { - - palSetPad(spip->config->ssport, spip->config->sspad); -} - -/** - * @brief Ignores data on the SPI bus. - * @details This asynchronous function starts the transmission of a series of - * idle words on the SPI bus and ignores the received data. - * @post At the end of the operation the configured callback is invoked. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to be ignored - * - * @notapi - */ -void spi_lld_ignore(SPIDriver *spip, size_t n) { - - dmaStreamSetMemory0(spip->dmarx, &dummyrx); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode); - - dmaStreamSetMemory0(spip->dmatx, &dummytx); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Exchanges data on the SPI bus. - * @details This asynchronous function starts a simultaneous transmit/receive - * operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to be exchanged - * @param[in] txbuf the pointer to the transmit buffer - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void spi_lld_exchange(SPIDriver *spip, size_t n, - const void *txbuf, void *rxbuf) { - - dmaStreamSetMemory0(spip->dmarx, rxbuf); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode| STM32_DMA_CR_MINC); - - dmaStreamSetMemory0(spip->dmatx, txbuf); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Sends data over the SPI bus. - * @details This asynchronous function starts a transmit operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to send - * @param[in] txbuf the pointer to the transmit buffer - * - * @notapi - */ -void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { - - dmaStreamSetMemory0(spip->dmarx, &dummyrx); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode); - - dmaStreamSetMemory0(spip->dmatx, txbuf); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Receives data from the SPI bus. - * @details This asynchronous function starts a receive operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to receive - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { - - dmaStreamSetMemory0(spip->dmarx, rxbuf); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC); - - dmaStreamSetMemory0(spip->dmatx, &dummytx); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Exchanges one frame using a polled wait. - * @details This synchronous function exchanges one frame using a polled - * synchronization method. This function is useful when exchanging - * small amount of data on high speed channels, usually in this - * situation is much more efficient just wait for completion using - * polling than suspending the thread waiting for an interrupt. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] frame the data frame to send over the SPI bus - * @return The received data frame from the SPI bus. - */ -uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { - - spip->spi->DR = frame; - while ((spip->spi->SR & SPI_SR_RXNE) == 0) - ; - return spip->spi->DR; -} - -#endif /* HAL_USE_SPI */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv1/spi_lld.h b/os/hal/ports/STM32/LLD/SPIv1/spi_lld.h deleted file mode 100644 index b06ab86ad..000000000 --- a/os/hal/ports/STM32/LLD/SPIv1/spi_lld.h +++ /dev/null @@ -1,543 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SPIv1/spi_lld.h - * @brief STM32 SPI subsystem low level driver header. - * - * @addtogroup SPI - * @{ - */ - -#ifndef _SPI_LLD_H_ -#define _SPI_LLD_H_ - -#if HAL_USE_SPI || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief SPI1 driver enable switch. - * @details If set to @p TRUE the support for SPI1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI1) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI1 FALSE -#endif - -/** - * @brief SPI2 driver enable switch. - * @details If set to @p TRUE the support for SPI2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI2) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI2 FALSE -#endif - -/** - * @brief SPI3 driver enable switch. - * @details If set to @p TRUE the support for SPI3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI3) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI3 FALSE -#endif - -/** - * @brief SPI4 driver enable switch. - * @details If set to @p TRUE the support for SPI4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI4) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI4 FALSE -#endif - -/** - * @brief SPI5 driver enable switch. - * @details If set to @p TRUE the support for SPI5 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI5) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI5 FALSE -#endif - -/** - * @brief SPI6 driver enable switch. - * @details If set to @p TRUE the support for SPI6 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI6) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI6 FALSE -#endif - -/** - * @brief SPI1 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI1_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI2 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI2_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI3 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI3_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI4 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI4_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI5 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI5_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI6 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI6_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI6_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI1 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI1_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI2 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI2_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI3 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI3_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI4 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI4_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI5 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI5_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI5_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI6 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI6_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI6_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI DMA error hook. - */ -#if !defined(STM32_SPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1 -#error "SPI1 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI2 && !STM32_HAS_SPI2 -#error "SPI2 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI3 && !STM32_HAS_SPI3 -#error "SPI3 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI4 && !STM32_HAS_SPI4 -#error "SPI4 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI5 && !STM32_HAS_SPI5 -#error "SPI5 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI6 && !STM32_HAS_SPI6 -#error "SPI6 not present in the selected device" -#endif - -#if !STM32_SPI_USE_SPI1 && !STM32_SPI_USE_SPI2 && !STM32_SPI_USE_SPI3 && \ - !STM32_SPI_USE_SPI4 && !STM32_SPI_USE_SPI5 && !STM32_SPI_USE_SPI6 -#error "SPI driver activated but no SPI peripheral assigned" -#endif - -#if STM32_SPI_USE_SPI1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI1" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI2" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI3" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI4" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI5" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI6_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI6" -#endif - -#if STM32_SPI_USE_SPI1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI1" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI2" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI3" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI4" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI5_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI5" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI6_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI6" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_SPI_USE_SPI1 && (!defined(STM32_SPI_SPI1_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI1_TX_DMA_STREAM)) -#error "SPI1 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI2 && (!defined(STM32_SPI_SPI2_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI2_TX_DMA_STREAM)) -#error "SPI2 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI3 && (!defined(STM32_SPI_SPI3_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI3_TX_DMA_STREAM)) -#error "SPI3 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI4 && (!defined(STM32_SPI_SPI4_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI4_TX_DMA_STREAM)) -#error "SPI4 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI5 && (!defined(STM32_SPI_SPI5_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI5_TX_DMA_STREAM)) -#error "SPI5 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI6 && (!defined(STM32_SPI_SPI6_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI6_TX_DMA_STREAM)) -#error "SPI6 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_SPI_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 RX" -#endif - -#if STM32_SPI_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 TX" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 RX" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 TX" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 RX" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 TX" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_RX_DMA_STREAM, STM32_SPI4_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI4 RX" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_TX_DMA_STREAM, STM32_SPI4_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI4 TX" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_RX_DMA_STREAM, STM32_SPI5_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI5 RX" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_TX_DMA_STREAM, STM32_SPI5_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI5 TX" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_RX_DMA_STREAM, STM32_SPI6_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI6 RX" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_TX_DMA_STREAM, STM32_SPI6_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI6 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an SPI driver. - */ -typedef struct SPIDriver SPIDriver; - -/** - * @brief SPI notification callback type. - * - * @param[in] spip pointer to the @p SPIDriver object triggering the - * callback - */ -typedef void (*spicallback_t)(SPIDriver *spip); - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /** - * @brief Operation complete callback or @p NULL. - */ - spicallback_t end_cb; - /* End of the mandatory fields.*/ - /** - * @brief The chip select line port. - */ - ioportid_t ssport; - /** - * @brief The chip select line pad number. - */ - uint16_t sspad; - /** - * @brief SPI initialization data. - */ - uint16_t cr1; -} SPIConfig; - -/** - * @brief Structure representing an SPI driver. - */ -struct SPIDriver { - /** - * @brief Driver state. - */ - spistate_t state; - /** - * @brief Current configuration data. - */ - const SPIConfig *config; -#if SPI_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif /* SPI_USE_WAIT */ -#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the bus. - */ - mutex_t mutex; -#endif /* SPI_USE_MUTUAL_EXCLUSION */ -#if defined(SPI_DRIVER_EXT_FIELDS) - SPI_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the SPIx registers block. - */ - SPI_TypeDef *spi; - /** - * @brief Receive DMA stream. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA stream. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__) -extern SPIDriver SPID1; -#endif - -#if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__) -extern SPIDriver SPID2; -#endif - -#if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__) -extern SPIDriver SPID3; -#endif - -#if STM32_SPI_USE_SPI4 && !defined(__DOXYGEN__) -extern SPIDriver SPID4; -#endif - -#if STM32_SPI_USE_SPI5 && !defined(__DOXYGEN__) -extern SPIDriver SPID5; -#endif - -#if STM32_SPI_USE_SPI6 && !defined(__DOXYGEN__) -extern SPIDriver SPID6; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void spi_lld_init(void); - void spi_lld_start(SPIDriver *spip); - void spi_lld_stop(SPIDriver *spip); - void spi_lld_select(SPIDriver *spip); - void spi_lld_unselect(SPIDriver *spip); - void spi_lld_ignore(SPIDriver *spip, size_t n); - void spi_lld_exchange(SPIDriver *spip, size_t n, - const void *txbuf, void *rxbuf); - void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); - void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); - uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SPI */ - -#endif /* _SPI_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.c b/os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.c new file mode 100644 index 000000000..e1f75c50f --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.c @@ -0,0 +1,577 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file i2s_lld.c + * @brief STM32 I2S subsystem low level driver source. + * + * @addtogroup I2S + * @{ + */ + +#include "hal.h" + +#if HAL_USE_I2S || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define I2S1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_RX_DMA_STREAM, \ + STM32_SPI1_RX_DMA_CHN) + +#define I2S1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_TX_DMA_STREAM, \ + STM32_SPI1_TX_DMA_CHN) + +#define I2S2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_RX_DMA_STREAM, \ + STM32_SPI2_RX_DMA_CHN) + +#define I2S2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_TX_DMA_STREAM, \ + STM32_SPI2_TX_DMA_CHN) + +#define I2S3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_RX_DMA_STREAM, \ + STM32_SPI3_RX_DMA_CHN) + +#define I2S3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_TX_DMA_STREAM, \ + STM32_SPI3_TX_DMA_CHN) + +/* + * Static I2S settings for I2S1. + */ +#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG 0 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 +#endif +#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) +#define STM32_I2S1_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ + SPI_I2SCFGR_I2SCFG_0) +#endif +#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ + +/* + * Static I2S settings for I2S2. + */ +#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG 0 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 +#endif +#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) +#define STM32_I2S2_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ + SPI_I2SCFGR_I2SCFG_0) +#endif +#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ + +/* + * Static I2S settings for I2S3. + */ +#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG 0 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 +#endif +#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 +#endif +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) +#define STM32_I2S3_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ + SPI_I2SCFGR_I2SCFG_0) +#endif +#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief I2S1 driver identifier.*/ +#if STM32_I2S_USE_SPI1 || defined(__DOXYGEN__) +I2SDriver I2SD1; +#endif + +/** @brief I2S2 driver identifier.*/ +#if STM32_I2S_USE_SPI2 || defined(__DOXYGEN__) +I2SDriver I2SD2; +#endif + +/** @brief I2S3 driver identifier.*/ +#if STM32_I2S_USE_SPI3 || defined(__DOXYGEN__) +I2SDriver I2SD3; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) || \ + STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) || \ + STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) +/** + * @brief Shared end-of-rx service routine. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void i2s_lld_serve_rx_interrupt(I2SDriver *i2sp, uint32_t flags) { + + (void)i2sp; + + /* DMA errors handling.*/ +#if defined(STM32_I2S_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2S_DMA_ERROR_HOOK(i2sp); + } +#endif + + /* Callbacks handling, note it is portable code defined in the high + level driver.*/ + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _i2s_isr_full_code(i2sp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _i2s_isr_half_code(i2sp); + } +} +#endif + +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) || \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) || \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) +/** + * @brief Shared end-of-tx service routine. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void i2s_lld_serve_tx_interrupt(I2SDriver *i2sp, uint32_t flags) { + + (void)i2sp; + + /* DMA errors handling.*/ +#if defined(STM32_I2S_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_I2S_DMA_ERROR_HOOK(i2sp); + } +#endif + + /* Callbacks handling, note it is portable code defined in the high + level driver.*/ + if ((flags & STM32_DMA_ISR_TCIF) != 0) { + /* Transfer complete processing.*/ + _i2s_isr_full_code(i2sp); + } + else if ((flags & STM32_DMA_ISR_HTIF) != 0) { + /* Half transfer processing.*/ + _i2s_isr_half_code(i2sp); + } +} +#endif + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level I2S driver initialization. + * + * @notapi + */ +void i2s_lld_init(void) { + +#if STM32_I2S_USE_SPI1 + i2sObjectInit(&I2SD1); + I2SD1.spi = SPI1; + I2SD1.cfg = STM32_I2S1_CFGR_CFG; +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) + I2SD1.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI1_RX_DMA_STREAM); + I2SD1.rxdmamode = STM32_DMA_CR_CHSEL(I2S1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD1.dmarx = NULL; + I2SD1.rxdmamode = 0; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) + I2SD1.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI1_TX_DMA_STREAM); + I2SD1.txdmamode = STM32_DMA_CR_CHSEL(I2S1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD1.dmatx = NULL; + I2SD1.txdmamode = 0; +#endif +#endif + +#if STM32_I2S_USE_SPI2 + i2sObjectInit(&I2SD2); + I2SD2.spi = SPI2; + I2SD2.cfg = STM32_I2S2_CFGR_CFG; +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) + I2SD2.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI2_RX_DMA_STREAM); + I2SD2.rxdmamode = STM32_DMA_CR_CHSEL(I2S2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD2.dmarx = NULL; + I2SD2.rxdmamode = 0; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) + I2SD2.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI2_TX_DMA_STREAM); + I2SD2.txdmamode = STM32_DMA_CR_CHSEL(I2S2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD2.dmatx = NULL; + I2SD2.txdmamode = 0; +#endif +#endif + +#if STM32_I2S_USE_SPI3 + i2sObjectInit(&I2SD3); + I2SD3.spi = SPI3; + I2SD3.cfg = STM32_I2S3_CFGR_CFG; +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) + I2SD3.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI3_RX_DMA_STREAM); + I2SD3.rxdmamode = STM32_DMA_CR_CHSEL(I2S3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD3.dmarx = NULL; + I2SD3.rxdmamode = 0; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) + I2SD3.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI3_TX_DMA_STREAM); + I2SD3.txdmamode = STM32_DMA_CR_CHSEL(I2S3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_PSIZE_HWORD | + STM32_DMA_CR_MSIZE_HWORD | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | + STM32_DMA_CR_CIRC | + STM32_DMA_CR_HTIE | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#else + I2SD3.dmatx = NULL; + I2SD3.txdmamode = 0; +#endif +#endif +} + +/** + * @brief Configures and activates the I2S peripheral. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_start(I2SDriver *i2sp) { + + /* If in stopped state then enables the SPI and DMA clocks.*/ + if (i2sp->state == I2S_STOP) { + +#if STM32_I2S_USE_SPI1 + if (&I2SD1 == i2sp) { + bool b; + + /* Enabling I2S unit clock.*/ + rccEnableSPI1(FALSE); + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) + b = dmaStreamAllocate(i2sp->dmarx, + STM32_I2S_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_RXDMAEN; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) + b = dmaStreamAllocate(i2sp->dmatx, + STM32_I2S_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_TXDMAEN; +#endif + } +#endif + +#if STM32_I2S_USE_SPI2 + if (&I2SD2 == i2sp) { + bool b; + + /* Enabling I2S unit clock.*/ + rccEnableSPI2(FALSE); + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) + b = dmaStreamAllocate(i2sp->dmarx, + STM32_I2S_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_RXDMAEN; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) + b = dmaStreamAllocate(i2sp->dmatx, + STM32_I2S_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_TXDMAEN; +#endif + } +#endif + +#if STM32_I2S_USE_SPI3 + if (&I2SD3 == i2sp) { + bool b; + + /* Enabling I2S unit clock.*/ + rccEnableSPI3(FALSE); + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) + b = dmaStreamAllocate(i2sp->dmarx, + STM32_I2S_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_RXDMAEN; +#endif +#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) + b = dmaStreamAllocate(i2sp->dmatx, + STM32_I2S_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, + (void *)i2sp); + osalDbgAssert(!b, "stream already allocated"); + + /* CRs settings are done here because those never changes until + the driver is stopped.*/ + i2sp->spi->CR1 = 0; + i2sp->spi->CR2 = SPI_CR2_TXDMAEN; +#endif + } +#endif + } + + /* I2S (re)configuration.*/ + i2sp->spi->I2SPR = i2sp->config->i2spr; + i2sp->spi->I2SCFGR = i2sp->config->i2scfgr | i2sp->cfg | SPI_I2SCFGR_I2SMOD; +} + +/** + * @brief Deactivates the I2S peripheral. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_stop(I2SDriver *i2sp) { + + /* If in ready state then disables the SPI clock.*/ + if (i2sp->state == I2S_READY) { + + /* SPI disable.*/ + i2sp->spi->CR2 = 0; + if (NULL != i2sp->dmarx) + dmaStreamRelease(i2sp->dmarx); + if (NULL != i2sp->dmatx) + dmaStreamRelease(i2sp->dmatx); + +#if STM32_I2S_USE_SPI1 + if (&I2SD1 == i2sp) + rccDisableSPI1(FALSE); +#endif + +#if STM32_I2S_USE_SPI2 + if (&I2SD2 == i2sp) + rccDisableSPI2(FALSE); +#endif + +#if STM32_I2S_USE_SPI3 + if (&I2SD3 == i2sp) + rccDisableSPI3(FALSE); +#endif + } +} + +/** + * @brief Starts a I2S data exchange. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_start_exchange(I2SDriver *i2sp) { + size_t size = i2sp->config->size; + + /* In 32 bit modes the DMA has to perform double operations because fetches + are always performed using 16 bit accesses. + DATLEN CHLEN SIZE + 00 (16) 0 (16) 16 + 00 (16) 1 (32) 16 + 01 (24) X 32 + 10 (32) X 32 + 11 (NA) X NA + */ + if ((i2sp->config->i2scfgr & SPI_I2SCFGR_DATLEN) != 0) + size *= 2; + + /* RX DMA setup.*/ + if (NULL != i2sp->dmarx) { + dmaStreamSetMode(i2sp->dmarx, i2sp->rxdmamode); + dmaStreamSetPeripheral(i2sp->dmarx, &i2sp->spi->DR); + dmaStreamSetMemory0(i2sp->dmarx, i2sp->config->rx_buffer); + dmaStreamSetTransactionSize(i2sp->dmarx, size); + dmaStreamEnable(i2sp->dmarx); + } + + /* TX DMA setup.*/ + if (NULL != i2sp->dmatx) { + dmaStreamSetMode(i2sp->dmatx, i2sp->txdmamode); + dmaStreamSetPeripheral(i2sp->dmatx, &i2sp->spi->DR); + dmaStreamSetMemory0(i2sp->dmatx, i2sp->config->tx_buffer); + dmaStreamSetTransactionSize(i2sp->dmatx, size); + dmaStreamEnable(i2sp->dmatx); + } + + /* Starting transfer.*/ + i2sp->spi->I2SCFGR |= SPI_I2SCFGR_I2SE; +} + +/** + * @brief Stops the ongoing data exchange. + * @details The ongoing data exchange, if any, is stopped, if the driver + * was not active the function does nothing. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * + * @notapi + */ +void i2s_lld_stop_exchange(I2SDriver *i2sp) { + + /* Stop TX DMA, if enabled.*/ + if (NULL != i2sp->dmatx) { + dmaStreamDisable(i2sp->dmatx); + + /* From the RM: To switch off the I2S, by clearing I2SE, it is mandatory + to wait for TXE = 1 and BSY = 0.*/ + while ((i2sp->spi->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE) + ; + } + + /* Stop SPI/I2S peripheral.*/ + i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE; + + /* Stop RX DMA, if enabled.*/ + if (NULL != i2sp->dmarx) + dmaStreamDisable(i2sp->dmarx); +} + +#endif /* HAL_USE_I2S */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.h b/os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.h new file mode 100644 index 000000000..0941ae3a8 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv2/hal_i2s_lld.h @@ -0,0 +1,432 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file i2s_lld.h + * @brief STM32 I2S subsystem low level driver header. + * + * @addtogroup I2S + * @{ + */ + +#ifndef _I2S_LLD_H_ +#define _I2S_LLD_H_ + +#if HAL_USE_I2S || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Static I2S modes + * @{ + */ +#define STM32_I2S_MODE_SLAVE 0 +#define STM32_I2S_MODE_MASTER 1 +#define STM32_I2S_MODE_RX 2 +#define STM32_I2S_MODE_TX 4 +#define STM32_I2S_MODE_RXTX (STM32_I2S_MODE_RX | \ + STM32_I2S_MODE_TX) +/** @} */ + +/** + * @name Mode checks + * @{ + */ +#define STM32_I2S_IS_MASTER(mode) ((mode) & STM32_I2S_MODE_MASTER) +#define STM32_I2S_RX_ENABLED(mode) ((mode) & STM32_I2S_MODE_RX) +#define STM32_I2S_TX_ENABLED(mode) ((mode) & STM32_I2S_MODE_TX) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief I2S1 driver enable switch. + * @details If set to @p TRUE the support for I2S1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_I2S_USE_SPI1) || defined(__DOXYGEN__) +#define STM32_I2S_USE_SPI1 FALSE +#endif + +/** + * @brief I2S2 driver enable switch. + * @details If set to @p TRUE the support for I2S2 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_I2S_USE_SPI2) || defined(__DOXYGEN__) +#define STM32_I2S_USE_SPI2 FALSE +#endif + +/** + * @brief I2S3 driver enable switch. + * @details If set to @p TRUE the support for I2S3 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_I2S_USE_SPI3) || defined(__DOXYGEN__) +#define STM32_I2S_USE_SPI3 FALSE +#endif + +/** + * @brief I2S1 mode. + */ +#if !defined(STM32_I2S_SPI1_MODE) || defined(__DOXYGEN__) +#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#endif + +/** + * @brief I2S2 mode. + */ +#if !defined(STM32_I2S_SPI2_MODE) || defined(__DOXYGEN__) +#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#endif + +/** + * @brief I2S3 mode. + */ +#if !defined(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) +#define STM32_I2S_SPI3_MODE (STM32_I2S_MODE_MASTER | \ + STM32_I2S_MODE_RX) +#endif + +/** + * @brief I2S1 interrupt priority level setting. + */ +#if !defined(STM32_I2S_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI1_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2S2 interrupt priority level setting. + */ +#if !defined(STM32_I2S_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI2_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2S3 interrupt priority level setting. + */ +#if !defined(STM32_I2S_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI3_IRQ_PRIORITY 10 +#endif + +/** + * @brief I2S1 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_I2S_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI1_DMA_PRIORITY 1 +#endif + +/** + * @brief I2S2 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_I2S_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI2_DMA_PRIORITY 1 +#endif + +/** + * @brief I2S3 DMA priority (0..3|lowest..highest). + */ +#if !defined(STM32_I2S_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_I2S_SPI3_DMA_PRIORITY 1 +#endif + +/** + * @brief I2S DMA error hook. + */ +#if !defined(STM32_I2S_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_I2S_USE_SPI1 && !STM32_SPI1_SUPPORTS_I2S +#error "SPI1 does not support I2S mode" +#endif + +#if STM32_I2S_USE_SPI2 && !STM32_SPI2_SUPPORTS_I2S +#error "SPI2 does not support I2S mode" +#endif + +#if STM32_I2S_USE_SPI3 && !STM32_SPI3_SUPPORTS_I2S +#error "SPI3 does not support I2S mode" +#endif + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) && \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) +#error "I2S1 RX and TX mode not supported in this driver implementation" +#endif + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) && \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) +#error "I2S2 RX and TX mode not supported in this driver implementation" +#endif + +#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) && \ + STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) +#error "I2S3 RX and TX mode not supported in this driver implementation" +#endif + +#if STM32_I2S_USE_SPI1 && !STM32_HAS_SPI1 +#error "SPI1 not present in the selected device" +#endif + +#if STM32_I2S_USE_SPI2 && !STM32_HAS_SPI2 +#error "SPI2 not present in the selected device" +#endif + +#if STM32_I2S_USE_SPI3 && !STM32_HAS_SPI3 +#error "SPI3 not present in the selected device" +#endif + +#if !STM32_I2S_USE_SPI1 && !STM32_I2S_USE_SPI2 && !STM32_I2S_USE_SPI3 +#error "I2S driver activated but no SPI peripheral assigned" +#endif + +#if STM32_I2S_USE_SPI1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI1" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI2" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI3" +#endif + +#if STM32_I2S_USE_SPI1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI1" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI2" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI3" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_I2S_USE_SPI1 && (!defined(STM32_I2S_SPI1_RX_DMA_STREAM) || \ + !defined(STM32_I2S_SPI1_TX_DMA_STREAM)) +#error "SPI1 DMA streams not defined" +#endif + +#if STM32_I2S_USE_SPI2 && (!defined(STM32_I2S_SPI2_RX_DMA_STREAM) || \ + !defined(STM32_I2S_SPI2_TX_DMA_STREAM)) +#error "SPI2 DMA streams not defined" +#endif + +#if STM32_I2S_USE_SPI3 && (!defined(STM32_I2S_SPI3_RX_DMA_STREAM) || \ + !defined(STM32_I2S_SPI3_TX_DMA_STREAM)) +#error "SPI3 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_I2S_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 RX" +#endif + +#if STM32_I2S_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 TX" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 RX" +#endif + +#if STM32_I2S_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 TX" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 RX" +#endif + +#if STM32_I2S_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an I2S driver. + */ +typedef struct I2SDriver I2SDriver; + +/** + * @brief I2S notification callback type. + * + * @param[in] i2sp pointer to the @p I2SDriver object + * @param[in] offset offset in buffers of the data to read/write + * @param[in] n number of samples to read/write + */ +typedef void (*i2scallback_t)(I2SDriver *i2sp, size_t offset, size_t n); + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Transmission buffer pointer. + * @note Can be @p NULL if TX is not required. + */ + const void *tx_buffer; + /** + * @brief Receive buffer pointer. + * @note Can be @p NULL if RX is not required. + */ + void *rx_buffer; + /** + * @brief TX and RX buffers size as number of samples. + */ + size_t size; + /** + * @brief Callback function called during streaming. + */ + i2scallback_t end_cb; + /* End of the mandatory fields.*/ + /** + * @brief Configuration of the I2SCFGR register. + * @details See the STM32 reference manual, this register is used for + * the I2S configuration, the following bits must not be + * specified because handled directly by the driver: + * - I2SMOD + * - I2SE + * - I2SCFG + * . + */ + int16_t i2scfgr; + /** + * @brief Configuration of the I2SPR register. + * @details See the STM32 reference manual, this register is used for + * the I2S clock setup. + */ + int16_t i2spr; +} I2SConfig; + +/** + * @brief Structure representing an I2S driver. + */ +struct I2SDriver { + /** + * @brief Driver state. + */ + i2sstate_t state; + /** + * @brief Current configuration data. + */ + const I2SConfig *config; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the SPIx registers block. + */ + SPI_TypeDef *spi; + /** + * @brief Calculated part of the I2SCFGR register. + */ + uint16_t cfg; + /** + * @brief Receive DMA stream or @p NULL. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA stream or @p NULL. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_I2S_USE_SPI1 && !defined(__DOXYGEN__) +extern I2SDriver I2SD1; +#endif + +#if STM32_I2S_USE_SPI2 && !defined(__DOXYGEN__) +extern I2SDriver I2SD2; +#endif + +#if STM32_I2S_USE_SPI3 && !defined(__DOXYGEN__) +extern I2SDriver I2SD3; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void i2s_lld_init(void); + void i2s_lld_start(I2SDriver *i2sp); + void i2s_lld_stop(I2SDriver *i2sp); + void i2s_lld_start_exchange(I2SDriver *i2sp); + void i2s_lld_stop_exchange(I2SDriver *i2sp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_I2S */ + +#endif /* _I2S_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.c b/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.c new file mode 100644 index 000000000..ae1fbbd47 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.c @@ -0,0 +1,652 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SPIv2/spi_lld.c + * @brief STM32 SPI subsystem low level driver source. + * + * @addtogroup SPI + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define SPI1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_RX_DMA_STREAM, \ + STM32_SPI1_RX_DMA_CHN) + +#define SPI1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_TX_DMA_STREAM, \ + STM32_SPI1_TX_DMA_CHN) + +#define SPI2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_RX_DMA_STREAM, \ + STM32_SPI2_RX_DMA_CHN) + +#define SPI2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_TX_DMA_STREAM, \ + STM32_SPI2_TX_DMA_CHN) + +#define SPI3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_RX_DMA_STREAM, \ + STM32_SPI3_RX_DMA_CHN) + +#define SPI3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_TX_DMA_STREAM, \ + STM32_SPI3_TX_DMA_CHN) + +#define SPI4_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_RX_DMA_STREAM, \ + STM32_SPI4_RX_DMA_CHN) + +#define SPI4_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_TX_DMA_STREAM, \ + STM32_SPI4_TX_DMA_CHN) + +#define SPI5_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_RX_DMA_STREAM, \ + STM32_SPI5_RX_DMA_CHN) + +#define SPI5_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_TX_DMA_STREAM, \ + STM32_SPI5_TX_DMA_CHN) + +#define SPI6_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_RX_DMA_STREAM, \ + STM32_SPI6_RX_DMA_CHN) + +#define SPI6_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_TX_DMA_STREAM, \ + STM32_SPI6_TX_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief SPI1 driver identifier.*/ +#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__) +SPIDriver SPID1; +#endif + +/** @brief SPI2 driver identifier.*/ +#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__) +SPIDriver SPID2; +#endif + +/** @brief SPI3 driver identifier.*/ +#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__) +SPIDriver SPID3; +#endif + +/** @brief SPI4 driver identifier.*/ +#if STM32_SPI_USE_SPI4 || defined(__DOXYGEN__) +SPIDriver SPID4; +#endif + +/** @brief SPI5 driver identifier.*/ +#if STM32_SPI_USE_SPI5 || defined(__DOXYGEN__) +SPIDriver SPID5; +#endif + +/** @brief SPI6 driver identifier.*/ +#if STM32_SPI_USE_SPI6 || defined(__DOXYGEN__) +SPIDriver SPID6; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const uint16_t dummytx = 0xFFFFU; +static uint16_t dummyrx; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Shared end-of-rx service routine. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_SPI_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_SPI_DMA_ERROR_HOOK(spip); + } +#else + (void)flags; +#endif + + /* Stop everything.*/ + dmaStreamDisable(spip->dmatx); + dmaStreamDisable(spip->dmarx); + + /* Portable SPI ISR code defined in the high level driver, note, it is + a macro.*/ + _spi_isr_code(spip); +} + +/** + * @brief Shared end-of-tx service routine. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_SPI_DMA_ERROR_HOOK) + (void)spip; + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_SPI_DMA_ERROR_HOOK(spip); + } +#else + (void)spip; + (void)flags; +#endif +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level SPI driver initialization. + * + * @notapi + */ +void spi_lld_init(void) { + +#if STM32_SPI_USE_SPI1 + spiObjectInit(&SPID1); + SPID1.spi = SPI1; + SPID1.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM); + SPID1.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM); + SPID1.rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID1.txdmamode = STM32_DMA_CR_CHSEL(SPI1_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI2 + spiObjectInit(&SPID2); + SPID2.spi = SPI2; + SPID2.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM); + SPID2.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM); + SPID2.rxdmamode = STM32_DMA_CR_CHSEL(SPI2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID2.txdmamode = STM32_DMA_CR_CHSEL(SPI2_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI3 + spiObjectInit(&SPID3); + SPID3.spi = SPI3; + SPID3.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM); + SPID3.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM); + SPID3.rxdmamode = STM32_DMA_CR_CHSEL(SPI3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID3.txdmamode = STM32_DMA_CR_CHSEL(SPI3_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI4 + spiObjectInit(&SPID4); + SPID4.spi = SPI4; + SPID4.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI4_RX_DMA_STREAM); + SPID4.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI4_TX_DMA_STREAM); + SPID4.rxdmamode = STM32_DMA_CR_CHSEL(SPI4_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID4.txdmamode = STM32_DMA_CR_CHSEL(SPI4_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI5 + spiObjectInit(&SPID5); + SPID5.spi = SPI5; + SPID5.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI5_RX_DMA_STREAM); + SPID5.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI5_TX_DMA_STREAM); + SPID5.rxdmamode = STM32_DMA_CR_CHSEL(SPI5_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID5.txdmamode = STM32_DMA_CR_CHSEL(SPI5_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif + +#if STM32_SPI_USE_SPI6 + spiObjectInit(&SPID6); + SPID6.spi = SPI6; + SPID6.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI6_RX_DMA_STREAM); + SPID6.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI6_TX_DMA_STREAM); + SPID6.rxdmamode = STM32_DMA_CR_CHSEL(SPI6_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | + STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_TCIE | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; + SPID6.txdmamode = STM32_DMA_CR_CHSEL(SPI6_TX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | + STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_DMEIE | + STM32_DMA_CR_TEIE; +#endif +} + +/** + * @brief Configures and activates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_start(SPIDriver *spip) { + uint32_t ds; + + /* If in stopped state then enables the SPI and DMA clocks.*/ + if (spip->state == SPI_STOP) { +#if STM32_SPI_USE_SPI1 + if (&SPID1 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI1_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI1(FALSE); + } +#endif +#if STM32_SPI_USE_SPI2 + if (&SPID2 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI2_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI2(FALSE); + } +#endif +#if STM32_SPI_USE_SPI3 + if (&SPID3 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI3_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI3(FALSE); + } +#endif +#if STM32_SPI_USE_SPI4 + if (&SPID4 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI4_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI4_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI4(FALSE); + } +#endif +#if STM32_SPI_USE_SPI5 + if (&SPID5 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI5_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI5_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI5(FALSE); + } +#endif +#if STM32_SPI_USE_SPI6 + if (&SPID6 == spip) { + bool b; + b = dmaStreamAllocate(spip->dmarx, + STM32_SPI_SPI6_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(spip->dmatx, + STM32_SPI_SPI6_IRQ_PRIORITY, + (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, + (void *)spip); + osalDbgAssert(!b, "stream already allocated"); + rccEnableSPI6(FALSE); + } +#endif + + /* DMA setup.*/ + dmaStreamSetPeripheral(spip->dmarx, &spip->spi->DR); + dmaStreamSetPeripheral(spip->dmatx, &spip->spi->DR); + } + + /* Configuration-specific DMA setup.*/ + ds = spip->config->cr2 & SPI_CR2_DS; + if (!ds || (ds <= (SPI_CR2_DS_2 | SPI_CR2_DS_1 | SPI_CR2_DS_0))) { + /* Frame width is 8 bits or smaller.*/ + spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; + spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; + } + else { + /* Frame width is larger than 8 bits.*/ + spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | + STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + } + + /* SPI setup and enable.*/ + spip->spi->CR1 = 0; + spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR; + spip->spi->CR2 = spip->config->cr2 | SPI_CR2_FRXTH | SPI_CR2_SSOE | + SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; + spip->spi->CR1 |= SPI_CR1_SPE; +} + +/** + * @brief Deactivates the SPI peripheral. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_stop(SPIDriver *spip) { + + /* If in ready state then disables the SPI clock.*/ + if (spip->state == SPI_READY) { + + /* SPI disable.*/ + spip->spi->CR1 = 0; + spip->spi->CR2 = 0; + dmaStreamRelease(spip->dmarx); + dmaStreamRelease(spip->dmatx); + +#if STM32_SPI_USE_SPI1 + if (&SPID1 == spip) + rccDisableSPI1(FALSE); +#endif +#if STM32_SPI_USE_SPI2 + if (&SPID2 == spip) + rccDisableSPI2(FALSE); +#endif +#if STM32_SPI_USE_SPI3 + if (&SPID3 == spip) + rccDisableSPI3(FALSE); +#endif +#if STM32_SPI_USE_SPI4 + if (&SPID4 == spip) + rccDisableSPI4(FALSE); +#endif +#if STM32_SPI_USE_SPI5 + if (&SPID5 == spip) + rccDisableSPI5(FALSE); +#endif +#if STM32_SPI_USE_SPI6 + if (&SPID6 == spip) + rccDisableSPI6(FALSE); +#endif + } +} + +/** + * @brief Asserts the slave select signal and prepares for transfers. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_select(SPIDriver *spip) { + + palClearPad(spip->config->ssport, spip->config->sspad); +} + +/** + * @brief Deasserts the slave select signal. + * @details The previously selected peripheral is unselected. + * + * @param[in] spip pointer to the @p SPIDriver object + * + * @notapi + */ +void spi_lld_unselect(SPIDriver *spip) { + + palSetPad(spip->config->ssport, spip->config->sspad); +} + +/** + * @brief Ignores data on the SPI bus. + * @details This asynchronous function starts the transmission of a series of + * idle words on the SPI bus and ignores the received data. + * @post At the end of the operation the configured callback is invoked. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be ignored + * + * @notapi + */ +void spi_lld_ignore(SPIDriver *spip, size_t n) { + + dmaStreamSetMemory0(spip->dmarx, &dummyrx); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode); + + dmaStreamSetMemory0(spip->dmatx, &dummytx); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Exchanges data on the SPI bus. + * @details This asynchronous function starts a simultaneous transmit/receive + * operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to be exchanged + * @param[in] txbuf the pointer to the transmit buffer + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf) { + + dmaStreamSetMemory0(spip->dmarx, rxbuf); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC); + + dmaStreamSetMemory0(spip->dmatx, txbuf); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Sends data over the SPI bus. + * @details This asynchronous function starts a transmit operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @notapi + */ +void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { + + dmaStreamSetMemory0(spip->dmarx, &dummyrx); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode); + + dmaStreamSetMemory0(spip->dmatx, txbuf); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Receives data from the SPI bus. + * @details This asynchronous function starts a receive operation. + * @post At the end of the operation the configured callback is invoked. + * @note The buffers are organized as uint8_t arrays for data sizes below or + * equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] n number of words to receive + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { + + dmaStreamSetMemory0(spip->dmarx, rxbuf); + dmaStreamSetTransactionSize(spip->dmarx, n); + dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC); + + dmaStreamSetMemory0(spip->dmatx, &dummytx); + dmaStreamSetTransactionSize(spip->dmatx, n); + dmaStreamSetMode(spip->dmatx, spip->txdmamode); + + dmaStreamEnable(spip->dmarx); + dmaStreamEnable(spip->dmatx); +} + +/** + * @brief Exchanges one frame using a polled wait. + * @details This synchronous function exchanges one frame using a polled + * synchronization method. This function is useful when exchanging + * small amount of data on high speed channels, usually in this + * situation is much more efficient just wait for completion using + * polling than suspending the thread waiting for an interrupt. + * + * @param[in] spip pointer to the @p SPIDriver object + * @param[in] frame the data frame to send over the SPI bus + * @return The received data frame from the SPI bus. + */ +uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { + + /* + * Data register must be accessed with the appropriate data size. + * Byte size access (uint8_t *) for transactions that are <= 8-bit. + * Halfword size access (uint16_t) for transactions that are <= 8-bit. + */ + if ((spip->config->cr2 & SPI_CR2_DS) <= (SPI_CR2_DS_2 | + SPI_CR2_DS_1 | + SPI_CR2_DS_0)) { + volatile uint8_t *spidr = (volatile uint8_t *)&spip->spi->DR; + *spidr = (uint8_t)frame; + while ((spip->spi->SR & SPI_SR_RXNE) == 0) + ; + return (uint16_t)*spidr; + } + else { + spip->spi->DR = frame; + while ((spip->spi->SR & SPI_SR_RXNE) == 0) + ; + return spip->spi->DR; + } +} + +#endif /* HAL_USE_SPI */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h b/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h new file mode 100644 index 000000000..969bcf5b7 --- /dev/null +++ b/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h @@ -0,0 +1,547 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/SPIv2/spi_lld.h + * @brief STM32 SPI subsystem low level driver header. + * + * @addtogroup SPI + * @{ + */ + +#ifndef _SPI_LLD_H_ +#define _SPI_LLD_H_ + +#if HAL_USE_SPI || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SPI1 driver enable switch. + * @details If set to @p TRUE the support for SPI1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI1) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI1 FALSE +#endif + +/** + * @brief SPI2 driver enable switch. + * @details If set to @p TRUE the support for SPI2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI2) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI2 FALSE +#endif + +/** + * @brief SPI3 driver enable switch. + * @details If set to @p TRUE the support for SPI3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI3) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI3 FALSE +#endif + +/** + * @brief SPI4 driver enable switch. + * @details If set to @p TRUE the support for SPI4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI4) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI4 FALSE +#endif + +/** + * @brief SPI5 driver enable switch. + * @details If set to @p TRUE the support for SPI5 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI5) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI5 FALSE +#endif + +/** + * @brief SPI6 driver enable switch. + * @details If set to @p TRUE the support for SPI6 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SPI_USE_SPI6) || defined(__DOXYGEN__) +#define STM32_SPI_USE_SPI6 FALSE +#endif + +/** + * @brief SPI1 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI1_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI2 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI2_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI3 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI3_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI4 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI4_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI5 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI5_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI6 interrupt priority level setting. + */ +#if !defined(STM32_SPI_SPI6_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI6_IRQ_PRIORITY 10 +#endif + +/** + * @brief SPI1 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI1_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI2 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI2_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI3 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI3_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI4 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI4_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI5 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI5_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI5_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI6 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA streams but + * because of the streams ordering the RX stream has always priority + * over the TX stream. + */ +#if !defined(STM32_SPI_SPI6_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SPI_SPI6_DMA_PRIORITY 1 +#endif + +/** + * @brief SPI DMA error hook. + */ +#if !defined(STM32_SPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1 +#error "SPI1 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI2 && !STM32_HAS_SPI2 +#error "SPI2 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI3 && !STM32_HAS_SPI3 +#error "SPI3 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI4 && !STM32_HAS_SPI4 +#error "SPI4 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI5 && !STM32_HAS_SPI5 +#error "SPI5 not present in the selected device" +#endif + +#if STM32_SPI_USE_SPI6 && !STM32_HAS_SPI6 +#error "SPI6 not present in the selected device" +#endif + +#if !STM32_SPI_USE_SPI1 && !STM32_SPI_USE_SPI2 && !STM32_SPI_USE_SPI3 && \ + !STM32_SPI_USE_SPI4 && !STM32_SPI_USE_SPI5 && !STM32_SPI_USE_SPI6 +#error "SPI driver activated but no SPI peripheral assigned" +#endif + +#if STM32_SPI_USE_SPI1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI1" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI2" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI3" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI4" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI5" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI6_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to SPI6" +#endif + +#if STM32_SPI_USE_SPI1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI1" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI2" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI3" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI4" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI5_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI5" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI6_DMA_PRIORITY) +#error "Invalid DMA priority assigned to SPI6" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_SPI_USE_SPI1 && (!defined(STM32_SPI_SPI1_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI1_TX_DMA_STREAM)) +#error "SPI1 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI2 && (!defined(STM32_SPI_SPI2_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI2_TX_DMA_STREAM)) +#error "SPI2 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI3 && (!defined(STM32_SPI_SPI3_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI3_TX_DMA_STREAM)) +#error "SPI3 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI4 && (!defined(STM32_SPI_SPI4_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI4_TX_DMA_STREAM)) +#error "SPI4 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI5 && (!defined(STM32_SPI_SPI5_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI5_TX_DMA_STREAM)) +#error "SPI5 DMA streams not defined" +#endif + +#if STM32_SPI_USE_SPI6 && (!defined(STM32_SPI_SPI6_RX_DMA_STREAM) || \ + !defined(STM32_SPI_SPI6_TX_DMA_STREAM)) +#error "SPI6 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_SPI_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 RX" +#endif + +#if STM32_SPI_USE_SPI1 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI1 TX" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 RX" +#endif + +#if STM32_SPI_USE_SPI2 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI2 TX" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 RX" +#endif + +#if STM32_SPI_USE_SPI3 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI3 TX" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_RX_DMA_STREAM, STM32_SPI4_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI4 RX" +#endif + +#if STM32_SPI_USE_SPI4 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_TX_DMA_STREAM, STM32_SPI4_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI4 TX" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_RX_DMA_STREAM, STM32_SPI5_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI5 RX" +#endif + +#if STM32_SPI_USE_SPI5 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_TX_DMA_STREAM, STM32_SPI5_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI5 TX" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_RX_DMA_STREAM, STM32_SPI6_RX_DMA_MSK) +#error "invalid DMA stream associated to SPI6 RX" +#endif + +#if STM32_SPI_USE_SPI6 && \ + !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_TX_DMA_STREAM, STM32_SPI6_TX_DMA_MSK) +#error "invalid DMA stream associated to SPI6 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an SPI driver. + */ +typedef struct SPIDriver SPIDriver; + +/** + * @brief SPI notification callback type. + * + * @param[in] spip pointer to the @p SPIDriver object triggering the + * callback + */ +typedef void (*spicallback_t)(SPIDriver *spip); + +/** + * @brief Driver configuration structure. + */ +typedef struct { + /** + * @brief Operation complete callback or @p NULL. + */ + spicallback_t end_cb; + /* End of the mandatory fields.*/ + /** + * @brief The chip select line port. + */ + ioportid_t ssport; + /** + * @brief The chip select line pad number. + */ + uint16_t sspad; + /** + * @brief SPI CR1 register initialization data. + */ + uint16_t cr1; + /** + * @brief SPI CR2 register initialization data. + */ + uint16_t cr2; +} SPIConfig; + +/** + * @brief Structure representing an SPI driver. + */ +struct SPIDriver { + /** + * @brief Driver state. + */ + spistate_t state; + /** + * @brief Current configuration data. + */ + const SPIConfig *config; +#if SPI_USE_WAIT || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif /* SPI_USE_WAIT */ +#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* SPI_USE_MUTUAL_EXCLUSION */ +#if defined(SPI_DRIVER_EXT_FIELDS) + SPI_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the SPIx registers block. + */ + SPI_TypeDef *spi; + /** + * @brief Receive DMA stream. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA stream. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief RX DMA mode bit mask. + */ + uint32_t rxdmamode; + /** + * @brief TX DMA mode bit mask. + */ + uint32_t txdmamode; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__) +extern SPIDriver SPID1; +#endif + +#if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__) +extern SPIDriver SPID2; +#endif + +#if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__) +extern SPIDriver SPID3; +#endif + +#if STM32_SPI_USE_SPI4 && !defined(__DOXYGEN__) +extern SPIDriver SPID4; +#endif + +#if STM32_SPI_USE_SPI5 && !defined(__DOXYGEN__) +extern SPIDriver SPID5; +#endif + +#if STM32_SPI_USE_SPI6 && !defined(__DOXYGEN__) +extern SPIDriver SPID6; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void spi_lld_init(void); + void spi_lld_start(SPIDriver *spip); + void spi_lld_stop(SPIDriver *spip); + void spi_lld_select(SPIDriver *spip); + void spi_lld_unselect(SPIDriver *spip); + void spi_lld_ignore(SPIDriver *spip, size_t n); + void spi_lld_exchange(SPIDriver *spip, size_t n, + const void *txbuf, void *rxbuf); + void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); + void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); + uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SPI */ + +#endif /* _SPI_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/i2s_lld.c b/os/hal/ports/STM32/LLD/SPIv2/i2s_lld.c deleted file mode 100644 index e1f75c50f..000000000 --- a/os/hal/ports/STM32/LLD/SPIv2/i2s_lld.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file i2s_lld.c - * @brief STM32 I2S subsystem low level driver source. - * - * @addtogroup I2S - * @{ - */ - -#include "hal.h" - -#if HAL_USE_I2S || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define I2S1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_RX_DMA_STREAM, \ - STM32_SPI1_RX_DMA_CHN) - -#define I2S1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI1_TX_DMA_STREAM, \ - STM32_SPI1_TX_DMA_CHN) - -#define I2S2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_RX_DMA_STREAM, \ - STM32_SPI2_RX_DMA_CHN) - -#define I2S2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI2_TX_DMA_STREAM, \ - STM32_SPI2_TX_DMA_CHN) - -#define I2S3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_RX_DMA_STREAM, \ - STM32_SPI3_RX_DMA_CHN) - -#define I2S3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_I2S_SPI3_TX_DMA_STREAM, \ - STM32_SPI3_TX_DMA_CHN) - -/* - * Static I2S settings for I2S1. - */ -#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG 0 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 -#endif -#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) -#define STM32_I2S1_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ - SPI_I2SCFGR_I2SCFG_0) -#endif -#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI1_MODE) */ - -/* - * Static I2S settings for I2S2. - */ -#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG 0 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 -#endif -#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) -#define STM32_I2S2_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ - SPI_I2SCFGR_I2SCFG_0) -#endif -#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI2_MODE) */ - -/* - * Static I2S settings for I2S3. - */ -#if !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG 0 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_0 -#endif -#else /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG SPI_I2SCFGR_I2SCFG_1 -#endif -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) -#define STM32_I2S3_CFGR_CFG (SPI_I2SCFGR_I2SCFG_1 | \ - SPI_I2SCFGR_I2SCFG_0) -#endif -#endif /* !STM32_I2S_IS_MASTER(STM32_I2S_SPI3_MODE) */ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief I2S1 driver identifier.*/ -#if STM32_I2S_USE_SPI1 || defined(__DOXYGEN__) -I2SDriver I2SD1; -#endif - -/** @brief I2S2 driver identifier.*/ -#if STM32_I2S_USE_SPI2 || defined(__DOXYGEN__) -I2SDriver I2SD2; -#endif - -/** @brief I2S3 driver identifier.*/ -#if STM32_I2S_USE_SPI3 || defined(__DOXYGEN__) -I2SDriver I2SD3; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) || \ - STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) || \ - STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) -/** - * @brief Shared end-of-rx service routine. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void i2s_lld_serve_rx_interrupt(I2SDriver *i2sp, uint32_t flags) { - - (void)i2sp; - - /* DMA errors handling.*/ -#if defined(STM32_I2S_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2S_DMA_ERROR_HOOK(i2sp); - } -#endif - - /* Callbacks handling, note it is portable code defined in the high - level driver.*/ - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _i2s_isr_full_code(i2sp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _i2s_isr_half_code(i2sp); - } -} -#endif - -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) || \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) || \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) -/** - * @brief Shared end-of-tx service routine. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void i2s_lld_serve_tx_interrupt(I2SDriver *i2sp, uint32_t flags) { - - (void)i2sp; - - /* DMA errors handling.*/ -#if defined(STM32_I2S_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_I2S_DMA_ERROR_HOOK(i2sp); - } -#endif - - /* Callbacks handling, note it is portable code defined in the high - level driver.*/ - if ((flags & STM32_DMA_ISR_TCIF) != 0) { - /* Transfer complete processing.*/ - _i2s_isr_full_code(i2sp); - } - else if ((flags & STM32_DMA_ISR_HTIF) != 0) { - /* Half transfer processing.*/ - _i2s_isr_half_code(i2sp); - } -} -#endif - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level I2S driver initialization. - * - * @notapi - */ -void i2s_lld_init(void) { - -#if STM32_I2S_USE_SPI1 - i2sObjectInit(&I2SD1); - I2SD1.spi = SPI1; - I2SD1.cfg = STM32_I2S1_CFGR_CFG; -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) - I2SD1.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI1_RX_DMA_STREAM); - I2SD1.rxdmamode = STM32_DMA_CR_CHSEL(I2S1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD1.dmarx = NULL; - I2SD1.rxdmamode = 0; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) - I2SD1.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI1_TX_DMA_STREAM); - I2SD1.txdmamode = STM32_DMA_CR_CHSEL(I2S1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD1.dmatx = NULL; - I2SD1.txdmamode = 0; -#endif -#endif - -#if STM32_I2S_USE_SPI2 - i2sObjectInit(&I2SD2); - I2SD2.spi = SPI2; - I2SD2.cfg = STM32_I2S2_CFGR_CFG; -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) - I2SD2.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI2_RX_DMA_STREAM); - I2SD2.rxdmamode = STM32_DMA_CR_CHSEL(I2S2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD2.dmarx = NULL; - I2SD2.rxdmamode = 0; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) - I2SD2.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI2_TX_DMA_STREAM); - I2SD2.txdmamode = STM32_DMA_CR_CHSEL(I2S2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD2.dmatx = NULL; - I2SD2.txdmamode = 0; -#endif -#endif - -#if STM32_I2S_USE_SPI3 - i2sObjectInit(&I2SD3); - I2SD3.spi = SPI3; - I2SD3.cfg = STM32_I2S3_CFGR_CFG; -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) - I2SD3.dmarx = STM32_DMA_STREAM(STM32_I2S_SPI3_RX_DMA_STREAM); - I2SD3.rxdmamode = STM32_DMA_CR_CHSEL(I2S3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD3.dmarx = NULL; - I2SD3.rxdmamode = 0; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) - I2SD3.dmatx = STM32_DMA_STREAM(STM32_I2S_SPI3_TX_DMA_STREAM); - I2SD3.txdmamode = STM32_DMA_CR_CHSEL(I2S3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_I2S_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_PSIZE_HWORD | - STM32_DMA_CR_MSIZE_HWORD | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | - STM32_DMA_CR_CIRC | - STM32_DMA_CR_HTIE | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#else - I2SD3.dmatx = NULL; - I2SD3.txdmamode = 0; -#endif -#endif -} - -/** - * @brief Configures and activates the I2S peripheral. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_start(I2SDriver *i2sp) { - - /* If in stopped state then enables the SPI and DMA clocks.*/ - if (i2sp->state == I2S_STOP) { - -#if STM32_I2S_USE_SPI1 - if (&I2SD1 == i2sp) { - bool b; - - /* Enabling I2S unit clock.*/ - rccEnableSPI1(FALSE); - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) - b = dmaStreamAllocate(i2sp->dmarx, - STM32_I2S_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_RXDMAEN; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) - b = dmaStreamAllocate(i2sp->dmatx, - STM32_I2S_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_TXDMAEN; -#endif - } -#endif - -#if STM32_I2S_USE_SPI2 - if (&I2SD2 == i2sp) { - bool b; - - /* Enabling I2S unit clock.*/ - rccEnableSPI2(FALSE); - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) - b = dmaStreamAllocate(i2sp->dmarx, - STM32_I2S_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_RXDMAEN; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) - b = dmaStreamAllocate(i2sp->dmatx, - STM32_I2S_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_TXDMAEN; -#endif - } -#endif - -#if STM32_I2S_USE_SPI3 - if (&I2SD3 == i2sp) { - bool b; - - /* Enabling I2S unit clock.*/ - rccEnableSPI3(FALSE); - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) - b = dmaStreamAllocate(i2sp->dmarx, - STM32_I2S_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_rx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_RXDMAEN; -#endif -#if STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) - b = dmaStreamAllocate(i2sp->dmatx, - STM32_I2S_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)i2s_lld_serve_tx_interrupt, - (void *)i2sp); - osalDbgAssert(!b, "stream already allocated"); - - /* CRs settings are done here because those never changes until - the driver is stopped.*/ - i2sp->spi->CR1 = 0; - i2sp->spi->CR2 = SPI_CR2_TXDMAEN; -#endif - } -#endif - } - - /* I2S (re)configuration.*/ - i2sp->spi->I2SPR = i2sp->config->i2spr; - i2sp->spi->I2SCFGR = i2sp->config->i2scfgr | i2sp->cfg | SPI_I2SCFGR_I2SMOD; -} - -/** - * @brief Deactivates the I2S peripheral. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_stop(I2SDriver *i2sp) { - - /* If in ready state then disables the SPI clock.*/ - if (i2sp->state == I2S_READY) { - - /* SPI disable.*/ - i2sp->spi->CR2 = 0; - if (NULL != i2sp->dmarx) - dmaStreamRelease(i2sp->dmarx); - if (NULL != i2sp->dmatx) - dmaStreamRelease(i2sp->dmatx); - -#if STM32_I2S_USE_SPI1 - if (&I2SD1 == i2sp) - rccDisableSPI1(FALSE); -#endif - -#if STM32_I2S_USE_SPI2 - if (&I2SD2 == i2sp) - rccDisableSPI2(FALSE); -#endif - -#if STM32_I2S_USE_SPI3 - if (&I2SD3 == i2sp) - rccDisableSPI3(FALSE); -#endif - } -} - -/** - * @brief Starts a I2S data exchange. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_start_exchange(I2SDriver *i2sp) { - size_t size = i2sp->config->size; - - /* In 32 bit modes the DMA has to perform double operations because fetches - are always performed using 16 bit accesses. - DATLEN CHLEN SIZE - 00 (16) 0 (16) 16 - 00 (16) 1 (32) 16 - 01 (24) X 32 - 10 (32) X 32 - 11 (NA) X NA - */ - if ((i2sp->config->i2scfgr & SPI_I2SCFGR_DATLEN) != 0) - size *= 2; - - /* RX DMA setup.*/ - if (NULL != i2sp->dmarx) { - dmaStreamSetMode(i2sp->dmarx, i2sp->rxdmamode); - dmaStreamSetPeripheral(i2sp->dmarx, &i2sp->spi->DR); - dmaStreamSetMemory0(i2sp->dmarx, i2sp->config->rx_buffer); - dmaStreamSetTransactionSize(i2sp->dmarx, size); - dmaStreamEnable(i2sp->dmarx); - } - - /* TX DMA setup.*/ - if (NULL != i2sp->dmatx) { - dmaStreamSetMode(i2sp->dmatx, i2sp->txdmamode); - dmaStreamSetPeripheral(i2sp->dmatx, &i2sp->spi->DR); - dmaStreamSetMemory0(i2sp->dmatx, i2sp->config->tx_buffer); - dmaStreamSetTransactionSize(i2sp->dmatx, size); - dmaStreamEnable(i2sp->dmatx); - } - - /* Starting transfer.*/ - i2sp->spi->I2SCFGR |= SPI_I2SCFGR_I2SE; -} - -/** - * @brief Stops the ongoing data exchange. - * @details The ongoing data exchange, if any, is stopped, if the driver - * was not active the function does nothing. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * - * @notapi - */ -void i2s_lld_stop_exchange(I2SDriver *i2sp) { - - /* Stop TX DMA, if enabled.*/ - if (NULL != i2sp->dmatx) { - dmaStreamDisable(i2sp->dmatx); - - /* From the RM: To switch off the I2S, by clearing I2SE, it is mandatory - to wait for TXE = 1 and BSY = 0.*/ - while ((i2sp->spi->SR & (SPI_SR_TXE | SPI_SR_BSY)) != SPI_SR_TXE) - ; - } - - /* Stop SPI/I2S peripheral.*/ - i2sp->spi->I2SCFGR &= ~SPI_I2SCFGR_I2SE; - - /* Stop RX DMA, if enabled.*/ - if (NULL != i2sp->dmarx) - dmaStreamDisable(i2sp->dmarx); -} - -#endif /* HAL_USE_I2S */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/i2s_lld.h b/os/hal/ports/STM32/LLD/SPIv2/i2s_lld.h deleted file mode 100644 index 0941ae3a8..000000000 --- a/os/hal/ports/STM32/LLD/SPIv2/i2s_lld.h +++ /dev/null @@ -1,432 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file i2s_lld.h - * @brief STM32 I2S subsystem low level driver header. - * - * @addtogroup I2S - * @{ - */ - -#ifndef _I2S_LLD_H_ -#define _I2S_LLD_H_ - -#if HAL_USE_I2S || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name Static I2S modes - * @{ - */ -#define STM32_I2S_MODE_SLAVE 0 -#define STM32_I2S_MODE_MASTER 1 -#define STM32_I2S_MODE_RX 2 -#define STM32_I2S_MODE_TX 4 -#define STM32_I2S_MODE_RXTX (STM32_I2S_MODE_RX | \ - STM32_I2S_MODE_TX) -/** @} */ - -/** - * @name Mode checks - * @{ - */ -#define STM32_I2S_IS_MASTER(mode) ((mode) & STM32_I2S_MODE_MASTER) -#define STM32_I2S_RX_ENABLED(mode) ((mode) & STM32_I2S_MODE_RX) -#define STM32_I2S_TX_ENABLED(mode) ((mode) & STM32_I2S_MODE_TX) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief I2S1 driver enable switch. - * @details If set to @p TRUE the support for I2S1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_I2S_USE_SPI1) || defined(__DOXYGEN__) -#define STM32_I2S_USE_SPI1 FALSE -#endif - -/** - * @brief I2S2 driver enable switch. - * @details If set to @p TRUE the support for I2S2 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_I2S_USE_SPI2) || defined(__DOXYGEN__) -#define STM32_I2S_USE_SPI2 FALSE -#endif - -/** - * @brief I2S3 driver enable switch. - * @details If set to @p TRUE the support for I2S3 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_I2S_USE_SPI3) || defined(__DOXYGEN__) -#define STM32_I2S_USE_SPI3 FALSE -#endif - -/** - * @brief I2S1 mode. - */ -#if !defined(STM32_I2S_SPI1_MODE) || defined(__DOXYGEN__) -#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \ - STM32_I2S_MODE_RX) -#endif - -/** - * @brief I2S2 mode. - */ -#if !defined(STM32_I2S_SPI2_MODE) || defined(__DOXYGEN__) -#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \ - STM32_I2S_MODE_RX) -#endif - -/** - * @brief I2S3 mode. - */ -#if !defined(STM32_I2S_SPI3_MODE) || defined(__DOXYGEN__) -#define STM32_I2S_SPI3_MODE (STM32_I2S_MODE_MASTER | \ - STM32_I2S_MODE_RX) -#endif - -/** - * @brief I2S1 interrupt priority level setting. - */ -#if !defined(STM32_I2S_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI1_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2S2 interrupt priority level setting. - */ -#if !defined(STM32_I2S_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI2_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2S3 interrupt priority level setting. - */ -#if !defined(STM32_I2S_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI3_IRQ_PRIORITY 10 -#endif - -/** - * @brief I2S1 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_I2S_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI1_DMA_PRIORITY 1 -#endif - -/** - * @brief I2S2 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_I2S_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI2_DMA_PRIORITY 1 -#endif - -/** - * @brief I2S3 DMA priority (0..3|lowest..highest). - */ -#if !defined(STM32_I2S_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_I2S_SPI3_DMA_PRIORITY 1 -#endif - -/** - * @brief I2S DMA error hook. - */ -#if !defined(STM32_I2S_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_I2S_USE_SPI1 && !STM32_SPI1_SUPPORTS_I2S -#error "SPI1 does not support I2S mode" -#endif - -#if STM32_I2S_USE_SPI2 && !STM32_SPI2_SUPPORTS_I2S -#error "SPI2 does not support I2S mode" -#endif - -#if STM32_I2S_USE_SPI3 && !STM32_SPI3_SUPPORTS_I2S -#error "SPI3 does not support I2S mode" -#endif - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI1_MODE) && \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI1_MODE) -#error "I2S1 RX and TX mode not supported in this driver implementation" -#endif - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI2_MODE) && \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI2_MODE) -#error "I2S2 RX and TX mode not supported in this driver implementation" -#endif - -#if STM32_I2S_RX_ENABLED(STM32_I2S_SPI3_MODE) && \ - STM32_I2S_TX_ENABLED(STM32_I2S_SPI3_MODE) -#error "I2S3 RX and TX mode not supported in this driver implementation" -#endif - -#if STM32_I2S_USE_SPI1 && !STM32_HAS_SPI1 -#error "SPI1 not present in the selected device" -#endif - -#if STM32_I2S_USE_SPI2 && !STM32_HAS_SPI2 -#error "SPI2 not present in the selected device" -#endif - -#if STM32_I2S_USE_SPI3 && !STM32_HAS_SPI3 -#error "SPI3 not present in the selected device" -#endif - -#if !STM32_I2S_USE_SPI1 && !STM32_I2S_USE_SPI2 && !STM32_I2S_USE_SPI3 -#error "I2S driver activated but no SPI peripheral assigned" -#endif - -#if STM32_I2S_USE_SPI1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI1" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI2" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_I2S_SPI3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI3" -#endif - -#if STM32_I2S_USE_SPI1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI1" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI2" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_I2S_SPI3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI3" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_I2S_USE_SPI1 && (!defined(STM32_I2S_SPI1_RX_DMA_STREAM) || \ - !defined(STM32_I2S_SPI1_TX_DMA_STREAM)) -#error "SPI1 DMA streams not defined" -#endif - -#if STM32_I2S_USE_SPI2 && (!defined(STM32_I2S_SPI2_RX_DMA_STREAM) || \ - !defined(STM32_I2S_SPI2_TX_DMA_STREAM)) -#error "SPI2 DMA streams not defined" -#endif - -#if STM32_I2S_USE_SPI3 && (!defined(STM32_I2S_SPI3_RX_DMA_STREAM) || \ - !defined(STM32_I2S_SPI3_TX_DMA_STREAM)) -#error "SPI3 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_I2S_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 RX" -#endif - -#if STM32_I2S_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 TX" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 RX" -#endif - -#if STM32_I2S_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 TX" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 RX" -#endif - -#if STM32_I2S_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_I2S_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an I2S driver. - */ -typedef struct I2SDriver I2SDriver; - -/** - * @brief I2S notification callback type. - * - * @param[in] i2sp pointer to the @p I2SDriver object - * @param[in] offset offset in buffers of the data to read/write - * @param[in] n number of samples to read/write - */ -typedef void (*i2scallback_t)(I2SDriver *i2sp, size_t offset, size_t n); - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Transmission buffer pointer. - * @note Can be @p NULL if TX is not required. - */ - const void *tx_buffer; - /** - * @brief Receive buffer pointer. - * @note Can be @p NULL if RX is not required. - */ - void *rx_buffer; - /** - * @brief TX and RX buffers size as number of samples. - */ - size_t size; - /** - * @brief Callback function called during streaming. - */ - i2scallback_t end_cb; - /* End of the mandatory fields.*/ - /** - * @brief Configuration of the I2SCFGR register. - * @details See the STM32 reference manual, this register is used for - * the I2S configuration, the following bits must not be - * specified because handled directly by the driver: - * - I2SMOD - * - I2SE - * - I2SCFG - * . - */ - int16_t i2scfgr; - /** - * @brief Configuration of the I2SPR register. - * @details See the STM32 reference manual, this register is used for - * the I2S clock setup. - */ - int16_t i2spr; -} I2SConfig; - -/** - * @brief Structure representing an I2S driver. - */ -struct I2SDriver { - /** - * @brief Driver state. - */ - i2sstate_t state; - /** - * @brief Current configuration data. - */ - const I2SConfig *config; - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the SPIx registers block. - */ - SPI_TypeDef *spi; - /** - * @brief Calculated part of the I2SCFGR register. - */ - uint16_t cfg; - /** - * @brief Receive DMA stream or @p NULL. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA stream or @p NULL. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_I2S_USE_SPI1 && !defined(__DOXYGEN__) -extern I2SDriver I2SD1; -#endif - -#if STM32_I2S_USE_SPI2 && !defined(__DOXYGEN__) -extern I2SDriver I2SD2; -#endif - -#if STM32_I2S_USE_SPI3 && !defined(__DOXYGEN__) -extern I2SDriver I2SD3; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void i2s_lld_init(void); - void i2s_lld_start(I2SDriver *i2sp); - void i2s_lld_stop(I2SDriver *i2sp); - void i2s_lld_start_exchange(I2SDriver *i2sp); - void i2s_lld_stop_exchange(I2SDriver *i2sp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_I2S */ - -#endif /* _I2S_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/spi_lld.c b/os/hal/ports/STM32/LLD/SPIv2/spi_lld.c deleted file mode 100644 index ae1fbbd47..000000000 --- a/os/hal/ports/STM32/LLD/SPIv2/spi_lld.c +++ /dev/null @@ -1,652 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SPIv2/spi_lld.c - * @brief STM32 SPI subsystem low level driver source. - * - * @addtogroup SPI - * @{ - */ - -#include "hal.h" - -#if HAL_USE_SPI || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define SPI1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_RX_DMA_STREAM, \ - STM32_SPI1_RX_DMA_CHN) - -#define SPI1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI1_TX_DMA_STREAM, \ - STM32_SPI1_TX_DMA_CHN) - -#define SPI2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_RX_DMA_STREAM, \ - STM32_SPI2_RX_DMA_CHN) - -#define SPI2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI2_TX_DMA_STREAM, \ - STM32_SPI2_TX_DMA_CHN) - -#define SPI3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_RX_DMA_STREAM, \ - STM32_SPI3_RX_DMA_CHN) - -#define SPI3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI3_TX_DMA_STREAM, \ - STM32_SPI3_TX_DMA_CHN) - -#define SPI4_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_RX_DMA_STREAM, \ - STM32_SPI4_RX_DMA_CHN) - -#define SPI4_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI4_TX_DMA_STREAM, \ - STM32_SPI4_TX_DMA_CHN) - -#define SPI5_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_RX_DMA_STREAM, \ - STM32_SPI5_RX_DMA_CHN) - -#define SPI5_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI5_TX_DMA_STREAM, \ - STM32_SPI5_TX_DMA_CHN) - -#define SPI6_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_RX_DMA_STREAM, \ - STM32_SPI6_RX_DMA_CHN) - -#define SPI6_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_SPI_SPI6_TX_DMA_STREAM, \ - STM32_SPI6_TX_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief SPI1 driver identifier.*/ -#if STM32_SPI_USE_SPI1 || defined(__DOXYGEN__) -SPIDriver SPID1; -#endif - -/** @brief SPI2 driver identifier.*/ -#if STM32_SPI_USE_SPI2 || defined(__DOXYGEN__) -SPIDriver SPID2; -#endif - -/** @brief SPI3 driver identifier.*/ -#if STM32_SPI_USE_SPI3 || defined(__DOXYGEN__) -SPIDriver SPID3; -#endif - -/** @brief SPI4 driver identifier.*/ -#if STM32_SPI_USE_SPI4 || defined(__DOXYGEN__) -SPIDriver SPID4; -#endif - -/** @brief SPI5 driver identifier.*/ -#if STM32_SPI_USE_SPI5 || defined(__DOXYGEN__) -SPIDriver SPID5; -#endif - -/** @brief SPI6 driver identifier.*/ -#if STM32_SPI_USE_SPI6 || defined(__DOXYGEN__) -SPIDriver SPID6; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -static const uint16_t dummytx = 0xFFFFU; -static uint16_t dummyrx; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Shared end-of-rx service routine. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void spi_lld_serve_rx_interrupt(SPIDriver *spip, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_SPI_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_SPI_DMA_ERROR_HOOK(spip); - } -#else - (void)flags; -#endif - - /* Stop everything.*/ - dmaStreamDisable(spip->dmatx); - dmaStreamDisable(spip->dmarx); - - /* Portable SPI ISR code defined in the high level driver, note, it is - a macro.*/ - _spi_isr_code(spip); -} - -/** - * @brief Shared end-of-tx service routine. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void spi_lld_serve_tx_interrupt(SPIDriver *spip, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_SPI_DMA_ERROR_HOOK) - (void)spip; - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_SPI_DMA_ERROR_HOOK(spip); - } -#else - (void)spip; - (void)flags; -#endif -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level SPI driver initialization. - * - * @notapi - */ -void spi_lld_init(void) { - -#if STM32_SPI_USE_SPI1 - spiObjectInit(&SPID1); - SPID1.spi = SPI1; - SPID1.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM); - SPID1.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM); - SPID1.rxdmamode = STM32_DMA_CR_CHSEL(SPI1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID1.txdmamode = STM32_DMA_CR_CHSEL(SPI1_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI1_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI2 - spiObjectInit(&SPID2); - SPID2.spi = SPI2; - SPID2.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM); - SPID2.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM); - SPID2.rxdmamode = STM32_DMA_CR_CHSEL(SPI2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID2.txdmamode = STM32_DMA_CR_CHSEL(SPI2_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI2_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI3 - spiObjectInit(&SPID3); - SPID3.spi = SPI3; - SPID3.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM); - SPID3.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM); - SPID3.rxdmamode = STM32_DMA_CR_CHSEL(SPI3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID3.txdmamode = STM32_DMA_CR_CHSEL(SPI3_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI3_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI4 - spiObjectInit(&SPID4); - SPID4.spi = SPI4; - SPID4.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI4_RX_DMA_STREAM); - SPID4.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI4_TX_DMA_STREAM); - SPID4.rxdmamode = STM32_DMA_CR_CHSEL(SPI4_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID4.txdmamode = STM32_DMA_CR_CHSEL(SPI4_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI4_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI5 - spiObjectInit(&SPID5); - SPID5.spi = SPI5; - SPID5.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI5_RX_DMA_STREAM); - SPID5.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI5_TX_DMA_STREAM); - SPID5.rxdmamode = STM32_DMA_CR_CHSEL(SPI5_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID5.txdmamode = STM32_DMA_CR_CHSEL(SPI5_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI5_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif - -#if STM32_SPI_USE_SPI6 - spiObjectInit(&SPID6); - SPID6.spi = SPI6; - SPID6.dmarx = STM32_DMA_STREAM(STM32_SPI_SPI6_RX_DMA_STREAM); - SPID6.dmatx = STM32_DMA_STREAM(STM32_SPI_SPI6_TX_DMA_STREAM); - SPID6.rxdmamode = STM32_DMA_CR_CHSEL(SPI6_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | - STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_TCIE | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; - SPID6.txdmamode = STM32_DMA_CR_CHSEL(SPI6_TX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_SPI_SPI6_DMA_PRIORITY) | - STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_DMEIE | - STM32_DMA_CR_TEIE; -#endif -} - -/** - * @brief Configures and activates the SPI peripheral. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_start(SPIDriver *spip) { - uint32_t ds; - - /* If in stopped state then enables the SPI and DMA clocks.*/ - if (spip->state == SPI_STOP) { -#if STM32_SPI_USE_SPI1 - if (&SPID1 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI1_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI1(FALSE); - } -#endif -#if STM32_SPI_USE_SPI2 - if (&SPID2 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI2_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI2(FALSE); - } -#endif -#if STM32_SPI_USE_SPI3 - if (&SPID3 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI3_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI3(FALSE); - } -#endif -#if STM32_SPI_USE_SPI4 - if (&SPID4 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI4_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI4_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI4(FALSE); - } -#endif -#if STM32_SPI_USE_SPI5 - if (&SPID5 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI5_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI5_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI5(FALSE); - } -#endif -#if STM32_SPI_USE_SPI6 - if (&SPID6 == spip) { - bool b; - b = dmaStreamAllocate(spip->dmarx, - STM32_SPI_SPI6_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_rx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(spip->dmatx, - STM32_SPI_SPI6_IRQ_PRIORITY, - (stm32_dmaisr_t)spi_lld_serve_tx_interrupt, - (void *)spip); - osalDbgAssert(!b, "stream already allocated"); - rccEnableSPI6(FALSE); - } -#endif - - /* DMA setup.*/ - dmaStreamSetPeripheral(spip->dmarx, &spip->spi->DR); - dmaStreamSetPeripheral(spip->dmatx, &spip->spi->DR); - } - - /* Configuration-specific DMA setup.*/ - ds = spip->config->cr2 & SPI_CR2_DS; - if (!ds || (ds <= (SPI_CR2_DS_2 | SPI_CR2_DS_1 | SPI_CR2_DS_0))) { - /* Frame width is 8 bits or smaller.*/ - spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; - spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_BYTE | STM32_DMA_CR_MSIZE_BYTE; - } - else { - /* Frame width is larger than 8 bits.*/ - spip->rxdmamode = (spip->rxdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - spip->txdmamode = (spip->txdmamode & ~STM32_DMA_CR_SIZE_MASK) | - STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - } - - /* SPI setup and enable.*/ - spip->spi->CR1 = 0; - spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR; - spip->spi->CR2 = spip->config->cr2 | SPI_CR2_FRXTH | SPI_CR2_SSOE | - SPI_CR2_RXDMAEN | SPI_CR2_TXDMAEN; - spip->spi->CR1 |= SPI_CR1_SPE; -} - -/** - * @brief Deactivates the SPI peripheral. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_stop(SPIDriver *spip) { - - /* If in ready state then disables the SPI clock.*/ - if (spip->state == SPI_READY) { - - /* SPI disable.*/ - spip->spi->CR1 = 0; - spip->spi->CR2 = 0; - dmaStreamRelease(spip->dmarx); - dmaStreamRelease(spip->dmatx); - -#if STM32_SPI_USE_SPI1 - if (&SPID1 == spip) - rccDisableSPI1(FALSE); -#endif -#if STM32_SPI_USE_SPI2 - if (&SPID2 == spip) - rccDisableSPI2(FALSE); -#endif -#if STM32_SPI_USE_SPI3 - if (&SPID3 == spip) - rccDisableSPI3(FALSE); -#endif -#if STM32_SPI_USE_SPI4 - if (&SPID4 == spip) - rccDisableSPI4(FALSE); -#endif -#if STM32_SPI_USE_SPI5 - if (&SPID5 == spip) - rccDisableSPI5(FALSE); -#endif -#if STM32_SPI_USE_SPI6 - if (&SPID6 == spip) - rccDisableSPI6(FALSE); -#endif - } -} - -/** - * @brief Asserts the slave select signal and prepares for transfers. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_select(SPIDriver *spip) { - - palClearPad(spip->config->ssport, spip->config->sspad); -} - -/** - * @brief Deasserts the slave select signal. - * @details The previously selected peripheral is unselected. - * - * @param[in] spip pointer to the @p SPIDriver object - * - * @notapi - */ -void spi_lld_unselect(SPIDriver *spip) { - - palSetPad(spip->config->ssport, spip->config->sspad); -} - -/** - * @brief Ignores data on the SPI bus. - * @details This asynchronous function starts the transmission of a series of - * idle words on the SPI bus and ignores the received data. - * @post At the end of the operation the configured callback is invoked. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to be ignored - * - * @notapi - */ -void spi_lld_ignore(SPIDriver *spip, size_t n) { - - dmaStreamSetMemory0(spip->dmarx, &dummyrx); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode); - - dmaStreamSetMemory0(spip->dmatx, &dummytx); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Exchanges data on the SPI bus. - * @details This asynchronous function starts a simultaneous transmit/receive - * operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to be exchanged - * @param[in] txbuf the pointer to the transmit buffer - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void spi_lld_exchange(SPIDriver *spip, size_t n, - const void *txbuf, void *rxbuf) { - - dmaStreamSetMemory0(spip->dmarx, rxbuf); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC); - - dmaStreamSetMemory0(spip->dmatx, txbuf); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Sends data over the SPI bus. - * @details This asynchronous function starts a transmit operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to send - * @param[in] txbuf the pointer to the transmit buffer - * - * @notapi - */ -void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) { - - dmaStreamSetMemory0(spip->dmarx, &dummyrx); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode); - - dmaStreamSetMemory0(spip->dmatx, txbuf); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode | STM32_DMA_CR_MINC); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Receives data from the SPI bus. - * @details This asynchronous function starts a receive operation. - * @post At the end of the operation the configured callback is invoked. - * @note The buffers are organized as uint8_t arrays for data sizes below or - * equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] n number of words to receive - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) { - - dmaStreamSetMemory0(spip->dmarx, rxbuf); - dmaStreamSetTransactionSize(spip->dmarx, n); - dmaStreamSetMode(spip->dmarx, spip->rxdmamode | STM32_DMA_CR_MINC); - - dmaStreamSetMemory0(spip->dmatx, &dummytx); - dmaStreamSetTransactionSize(spip->dmatx, n); - dmaStreamSetMode(spip->dmatx, spip->txdmamode); - - dmaStreamEnable(spip->dmarx); - dmaStreamEnable(spip->dmatx); -} - -/** - * @brief Exchanges one frame using a polled wait. - * @details This synchronous function exchanges one frame using a polled - * synchronization method. This function is useful when exchanging - * small amount of data on high speed channels, usually in this - * situation is much more efficient just wait for completion using - * polling than suspending the thread waiting for an interrupt. - * - * @param[in] spip pointer to the @p SPIDriver object - * @param[in] frame the data frame to send over the SPI bus - * @return The received data frame from the SPI bus. - */ -uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) { - - /* - * Data register must be accessed with the appropriate data size. - * Byte size access (uint8_t *) for transactions that are <= 8-bit. - * Halfword size access (uint16_t) for transactions that are <= 8-bit. - */ - if ((spip->config->cr2 & SPI_CR2_DS) <= (SPI_CR2_DS_2 | - SPI_CR2_DS_1 | - SPI_CR2_DS_0)) { - volatile uint8_t *spidr = (volatile uint8_t *)&spip->spi->DR; - *spidr = (uint8_t)frame; - while ((spip->spi->SR & SPI_SR_RXNE) == 0) - ; - return (uint16_t)*spidr; - } - else { - spip->spi->DR = frame; - while ((spip->spi->SR & SPI_SR_RXNE) == 0) - ; - return spip->spi->DR; - } -} - -#endif /* HAL_USE_SPI */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/SPIv2/spi_lld.h b/os/hal/ports/STM32/LLD/SPIv2/spi_lld.h deleted file mode 100644 index 969bcf5b7..000000000 --- a/os/hal/ports/STM32/LLD/SPIv2/spi_lld.h +++ /dev/null @@ -1,547 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/SPIv2/spi_lld.h - * @brief STM32 SPI subsystem low level driver header. - * - * @addtogroup SPI - * @{ - */ - -#ifndef _SPI_LLD_H_ -#define _SPI_LLD_H_ - -#if HAL_USE_SPI || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief SPI1 driver enable switch. - * @details If set to @p TRUE the support for SPI1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI1) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI1 FALSE -#endif - -/** - * @brief SPI2 driver enable switch. - * @details If set to @p TRUE the support for SPI2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI2) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI2 FALSE -#endif - -/** - * @brief SPI3 driver enable switch. - * @details If set to @p TRUE the support for SPI3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI3) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI3 FALSE -#endif - -/** - * @brief SPI4 driver enable switch. - * @details If set to @p TRUE the support for SPI4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI4) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI4 FALSE -#endif - -/** - * @brief SPI5 driver enable switch. - * @details If set to @p TRUE the support for SPI5 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI5) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI5 FALSE -#endif - -/** - * @brief SPI6 driver enable switch. - * @details If set to @p TRUE the support for SPI6 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SPI_USE_SPI6) || defined(__DOXYGEN__) -#define STM32_SPI_USE_SPI6 FALSE -#endif - -/** - * @brief SPI1 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI1_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI2 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI2_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI3 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI3_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI4 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI4_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI5 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI5_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI6 interrupt priority level setting. - */ -#if !defined(STM32_SPI_SPI6_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI6_IRQ_PRIORITY 10 -#endif - -/** - * @brief SPI1 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI1_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI2 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI2_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI3 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI3_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI4 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI4_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI5 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI5_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI5_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI6 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA streams but - * because of the streams ordering the RX stream has always priority - * over the TX stream. - */ -#if !defined(STM32_SPI_SPI6_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SPI_SPI6_DMA_PRIORITY 1 -#endif - -/** - * @brief SPI DMA error hook. - */ -#if !defined(STM32_SPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1 -#error "SPI1 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI2 && !STM32_HAS_SPI2 -#error "SPI2 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI3 && !STM32_HAS_SPI3 -#error "SPI3 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI4 && !STM32_HAS_SPI4 -#error "SPI4 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI5 && !STM32_HAS_SPI5 -#error "SPI5 not present in the selected device" -#endif - -#if STM32_SPI_USE_SPI6 && !STM32_HAS_SPI6 -#error "SPI6 not present in the selected device" -#endif - -#if !STM32_SPI_USE_SPI1 && !STM32_SPI_USE_SPI2 && !STM32_SPI_USE_SPI3 && \ - !STM32_SPI_USE_SPI4 && !STM32_SPI_USE_SPI5 && !STM32_SPI_USE_SPI6 -#error "SPI driver activated but no SPI peripheral assigned" -#endif - -#if STM32_SPI_USE_SPI1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI1" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI2" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI3" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI4" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI5" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SPI_SPI6_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to SPI6" -#endif - -#if STM32_SPI_USE_SPI1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI1" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI2" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI3" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI4" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI5_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI5" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI6_DMA_PRIORITY) -#error "Invalid DMA priority assigned to SPI6" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_SPI_USE_SPI1 && (!defined(STM32_SPI_SPI1_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI1_TX_DMA_STREAM)) -#error "SPI1 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI2 && (!defined(STM32_SPI_SPI2_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI2_TX_DMA_STREAM)) -#error "SPI2 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI3 && (!defined(STM32_SPI_SPI3_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI3_TX_DMA_STREAM)) -#error "SPI3 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI4 && (!defined(STM32_SPI_SPI4_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI4_TX_DMA_STREAM)) -#error "SPI4 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI5 && (!defined(STM32_SPI_SPI5_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI5_TX_DMA_STREAM)) -#error "SPI5 DMA streams not defined" -#endif - -#if STM32_SPI_USE_SPI6 && (!defined(STM32_SPI_SPI6_RX_DMA_STREAM) || \ - !defined(STM32_SPI_SPI6_TX_DMA_STREAM)) -#error "SPI6 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_SPI_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_RX_DMA_STREAM, STM32_SPI1_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 RX" -#endif - -#if STM32_SPI_USE_SPI1 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI1_TX_DMA_STREAM, STM32_SPI1_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI1 TX" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_RX_DMA_STREAM, STM32_SPI2_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 RX" -#endif - -#if STM32_SPI_USE_SPI2 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI2_TX_DMA_STREAM, STM32_SPI2_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI2 TX" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_RX_DMA_STREAM, STM32_SPI3_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 RX" -#endif - -#if STM32_SPI_USE_SPI3 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI3_TX_DMA_STREAM, STM32_SPI3_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI3 TX" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_RX_DMA_STREAM, STM32_SPI4_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI4 RX" -#endif - -#if STM32_SPI_USE_SPI4 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI4_TX_DMA_STREAM, STM32_SPI4_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI4 TX" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_RX_DMA_STREAM, STM32_SPI5_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI5 RX" -#endif - -#if STM32_SPI_USE_SPI5 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI5_TX_DMA_STREAM, STM32_SPI5_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI5 TX" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_RX_DMA_STREAM, STM32_SPI6_RX_DMA_MSK) -#error "invalid DMA stream associated to SPI6 RX" -#endif - -#if STM32_SPI_USE_SPI6 && \ - !STM32_DMA_IS_VALID_ID(STM32_SPI_SPI6_TX_DMA_STREAM, STM32_SPI6_TX_DMA_MSK) -#error "invalid DMA stream associated to SPI6 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an SPI driver. - */ -typedef struct SPIDriver SPIDriver; - -/** - * @brief SPI notification callback type. - * - * @param[in] spip pointer to the @p SPIDriver object triggering the - * callback - */ -typedef void (*spicallback_t)(SPIDriver *spip); - -/** - * @brief Driver configuration structure. - */ -typedef struct { - /** - * @brief Operation complete callback or @p NULL. - */ - spicallback_t end_cb; - /* End of the mandatory fields.*/ - /** - * @brief The chip select line port. - */ - ioportid_t ssport; - /** - * @brief The chip select line pad number. - */ - uint16_t sspad; - /** - * @brief SPI CR1 register initialization data. - */ - uint16_t cr1; - /** - * @brief SPI CR2 register initialization data. - */ - uint16_t cr2; -} SPIConfig; - -/** - * @brief Structure representing an SPI driver. - */ -struct SPIDriver { - /** - * @brief Driver state. - */ - spistate_t state; - /** - * @brief Current configuration data. - */ - const SPIConfig *config; -#if SPI_USE_WAIT || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif /* SPI_USE_WAIT */ -#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* SPI_USE_MUTUAL_EXCLUSION */ -#if defined(SPI_DRIVER_EXT_FIELDS) - SPI_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the SPIx registers block. - */ - SPI_TypeDef *spi; - /** - * @brief Receive DMA stream. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA stream. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief RX DMA mode bit mask. - */ - uint32_t rxdmamode; - /** - * @brief TX DMA mode bit mask. - */ - uint32_t txdmamode; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_SPI_USE_SPI1 && !defined(__DOXYGEN__) -extern SPIDriver SPID1; -#endif - -#if STM32_SPI_USE_SPI2 && !defined(__DOXYGEN__) -extern SPIDriver SPID2; -#endif - -#if STM32_SPI_USE_SPI3 && !defined(__DOXYGEN__) -extern SPIDriver SPID3; -#endif - -#if STM32_SPI_USE_SPI4 && !defined(__DOXYGEN__) -extern SPIDriver SPID4; -#endif - -#if STM32_SPI_USE_SPI5 && !defined(__DOXYGEN__) -extern SPIDriver SPID5; -#endif - -#if STM32_SPI_USE_SPI6 && !defined(__DOXYGEN__) -extern SPIDriver SPID6; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void spi_lld_init(void); - void spi_lld_start(SPIDriver *spip); - void spi_lld_stop(SPIDriver *spip); - void spi_lld_select(SPIDriver *spip); - void spi_lld_unselect(SPIDriver *spip); - void spi_lld_ignore(SPIDriver *spip, size_t n); - void spi_lld_exchange(SPIDriver *spip, size_t n, - const void *txbuf, void *rxbuf); - void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf); - void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf); - uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SPI */ - -#endif /* _SPI_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/gpt_lld.c b/os/hal/ports/STM32/LLD/TIMv1/gpt_lld.c deleted file mode 100644 index fd2a804c1..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/gpt_lld.c +++ /dev/null @@ -1,891 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/gpt_lld.c - * @brief STM32 GPT subsystem low level driver source. - * - * @addtogroup GPT - * @{ - */ - -#include "hal.h" - -#if HAL_USE_GPT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief GPTD1 driver identifier. - * @note The driver GPTD1 allocates the complex timer TIM1 when enabled. - */ -#if STM32_GPT_USE_TIM1 || defined(__DOXYGEN__) -GPTDriver GPTD1; -#endif - -/** - * @brief GPTD2 driver identifier. - * @note The driver GPTD2 allocates the timer TIM2 when enabled. - */ -#if STM32_GPT_USE_TIM2 || defined(__DOXYGEN__) -GPTDriver GPTD2; -#endif - -/** - * @brief GPTD3 driver identifier. - * @note The driver GPTD3 allocates the timer TIM3 when enabled. - */ -#if STM32_GPT_USE_TIM3 || defined(__DOXYGEN__) -GPTDriver GPTD3; -#endif - -/** - * @brief GPTD4 driver identifier. - * @note The driver GPTD4 allocates the timer TIM4 when enabled. - */ -#if STM32_GPT_USE_TIM4 || defined(__DOXYGEN__) -GPTDriver GPTD4; -#endif - -/** - * @brief GPTD5 driver identifier. - * @note The driver GPTD5 allocates the timer TIM5 when enabled. - */ -#if STM32_GPT_USE_TIM5 || defined(__DOXYGEN__) -GPTDriver GPTD5; -#endif - -/** - * @brief GPTD6 driver identifier. - * @note The driver GPTD6 allocates the timer TIM6 when enabled. - */ -#if STM32_GPT_USE_TIM6 || defined(__DOXYGEN__) -GPTDriver GPTD6; -#endif - -/** - * @brief GPTD7 driver identifier. - * @note The driver GPTD7 allocates the timer TIM7 when enabled. - */ -#if STM32_GPT_USE_TIM7 || defined(__DOXYGEN__) -GPTDriver GPTD7; -#endif - -/** - * @brief GPTD8 driver identifier. - * @note The driver GPTD8 allocates the timer TIM8 when enabled. - */ -#if STM32_GPT_USE_TIM8 || defined(__DOXYGEN__) -GPTDriver GPTD8; -#endif - -/** - * @brief GPTD9 driver identifier. - * @note The driver GPTD9 allocates the timer TIM9 when enabled. - */ -#if STM32_GPT_USE_TIM9 || defined(__DOXYGEN__) -GPTDriver GPTD9; -#endif - -/** - * @brief GPTD11 driver identifier. - * @note The driver GPTD11 allocates the timer TIM11 when enabled. - */ -#if STM32_GPT_USE_TIM11 || defined(__DOXYGEN__) -GPTDriver GPTD11; -#endif - -/** - * @brief GPTD12 driver identifier. - * @note The driver GPTD12 allocates the timer TIM12 when enabled. - */ -#if STM32_GPT_USE_TIM12 || defined(__DOXYGEN__) -GPTDriver GPTD12; -#endif - -/** - * @brief GPTD14 driver identifier. - * @note The driver GPTD14 allocates the timer TIM14 when enabled. - */ -#if STM32_GPT_USE_TIM14 || defined(__DOXYGEN__) -GPTDriver GPTD14; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_GPT_USE_TIM1 || defined(__DOXYGEN__) -#if !defined(STM32_TIM1_SUPPRESS_ISR) -#if !defined(STM32_TIM1_UP_HANDLER) -#error "STM32_TIM1_UP_HANDLER not defined" -#endif -/** - * @brief TIM2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM1_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM1 */ - -#if STM32_GPT_USE_TIM2 || defined(__DOXYGEN__) -#if !defined(STM32_TIM2_SUPPRESS_ISR) -#if !defined(STM32_TIM2_HANDLER) -#error "STM32_TIM2_HANDLER not defined" -#endif -/** - * @brief TIM2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM2_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM2 */ - -#if STM32_GPT_USE_TIM3 || defined(__DOXYGEN__) -#if !defined(STM32_TIM3_SUPPRESS_ISR) -#if !defined(STM32_TIM3_HANDLER) -#error "STM32_TIM3_HANDLER not defined" -#endif -/** - * @brief TIM3 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM3_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM3 */ - -#if STM32_GPT_USE_TIM4 || defined(__DOXYGEN__) -#if !defined(STM32_TIM4_SUPPRESS_ISR) -#if !defined(STM32_TIM4_HANDLER) -#error "STM32_TIM4_HANDLER not defined" -#endif -/** - * @brief TIM4 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM4_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM4 */ - -#if STM32_GPT_USE_TIM5 || defined(__DOXYGEN__) -#if !defined(STM32_TIM5_SUPPRESS_ISR) -#if !defined(STM32_TIM5_HANDLER) -#error "STM32_TIM5_HANDLER not defined" -#endif -/** - * @brief TIM5 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM5_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM5 */ - -#if STM32_GPT_USE_TIM6 || defined(__DOXYGEN__) -#if !defined(STM32_TIM6_SUPPRESS_ISR) -#if !defined(STM32_TIM6_HANDLER) -#error "STM32_TIM6_HANDLER not defined" -#endif -/** - * @brief TIM6 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM6_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD6); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM6_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM6 */ - -#if STM32_GPT_USE_TIM7 || defined(__DOXYGEN__) -#if !defined(STM32_TIM7_SUPPRESS_ISR) -#if !defined(STM32_TIM7_HANDLER) -#error "STM32_TIM7_HANDLER not defined" -#endif -/** - * @brief TIM7 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM7_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD7); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM7_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM7 */ - -#if STM32_GPT_USE_TIM8 || defined(__DOXYGEN__) -#if !defined(STM32_TIM8_SUPPRESS_ISR) -#if !defined(STM32_TIM8_UP_HANDLER) -#error "STM32_TIM8_UP_HANDLER not defined" -#endif -/** - * @brief TIM8 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD8); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM8_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM8 */ - -#if STM32_GPT_USE_TIM9 || defined(__DOXYGEN__) -#if !defined(STM32_TIM9_SUPPRESS_ISR) -#if !defined(STM32_TIM9_HANDLER) -#error "STM32_TIM9_HANDLER not defined" -#endif -/** - * @brief TIM9 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM9_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD9); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM9_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM9 */ - -#if STM32_GPT_USE_TIM11 || defined(__DOXYGEN__) -#if !defined(STM32_TIM11_SUPPRESS_ISR) -#if !defined(STM32_TIM11_HANDLER) -#error "STM32_TIM11_HANDLER not defined" -#endif -/** - * @brief TIM11 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM11_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD11); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM11_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM11 */ - -#if STM32_GPT_USE_TIM12 || defined(__DOXYGEN__) -#if !defined(STM32_TIM12_SUPPRESS_ISR) -#if !defined(STM32_TIM12_HANDLER) -#error "STM32_TIM12_HANDLER not defined" -#endif -/** - * @brief TIM12 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM12_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD12); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM12_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM12 */ - -#if STM32_GPT_USE_TIM14 || defined(__DOXYGEN__) -#if !defined(STM32_TIM14_SUPPRESS_ISR) -#if !defined(STM32_TIM14_HANDLER) -#error "STM32_TIM14_HANDLER not defined" -#endif -/** - * @brief TIM14 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM14_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - gpt_lld_serve_interrupt(&GPTD14); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM14_SUPPRESS_ISR) */ -#endif /* STM32_GPT_USE_TIM14 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level GPT driver initialization. - * - * @notapi - */ -void gpt_lld_init(void) { - -#if STM32_GPT_USE_TIM1 - /* Driver initialization.*/ - GPTD1.tim = STM32_TIM1; - gptObjectInit(&GPTD1); -#endif - -#if STM32_GPT_USE_TIM2 - /* Driver initialization.*/ - GPTD2.tim = STM32_TIM2; - gptObjectInit(&GPTD2); -#endif - -#if STM32_GPT_USE_TIM3 - /* Driver initialization.*/ - GPTD3.tim = STM32_TIM3; - gptObjectInit(&GPTD3); -#endif - -#if STM32_GPT_USE_TIM4 - /* Driver initialization.*/ - GPTD4.tim = STM32_TIM4; - gptObjectInit(&GPTD4); -#endif - -#if STM32_GPT_USE_TIM5 - /* Driver initialization.*/ - GPTD5.tim = STM32_TIM5; - gptObjectInit(&GPTD5); -#endif - -#if STM32_GPT_USE_TIM6 - /* Driver initialization.*/ - GPTD6.tim = STM32_TIM6; - gptObjectInit(&GPTD6); -#endif - -#if STM32_GPT_USE_TIM7 - /* Driver initialization.*/ - GPTD7.tim = STM32_TIM7; - gptObjectInit(&GPTD7); -#endif - -#if STM32_GPT_USE_TIM8 - /* Driver initialization.*/ - GPTD8.tim = STM32_TIM8; - gptObjectInit(&GPTD8); -#endif - -#if STM32_GPT_USE_TIM9 - /* Driver initialization.*/ - GPTD9.tim = STM32_TIM9; - gptObjectInit(&GPTD9); -#endif - -#if STM32_GPT_USE_TIM11 - /* Driver initialization.*/ - GPTD11.tim = STM32_TIM11; - gptObjectInit(&GPTD11); -#endif - -#if STM32_GPT_USE_TIM12 - /* Driver initialization.*/ - GPTD12.tim = STM32_TIM12; - gptObjectInit(&GPTD12); -#endif - -#if STM32_GPT_USE_TIM14 - /* Driver initialization.*/ - GPTD14.tim = STM32_TIM14; - gptObjectInit(&GPTD14); -#endif -} - -/** - * @brief Configures and activates the GPT peripheral. - * - * @param[in] gptp pointer to the @p GPTDriver object - * - * @notapi - */ -void gpt_lld_start(GPTDriver *gptp) { - uint16_t psc; - - if (gptp->state == GPT_STOP) { - /* Clock activation.*/ -#if STM32_GPT_USE_TIM1 - if (&GPTD1 == gptp) { - rccEnableTIM1(FALSE); - rccResetTIM1(); -#if !defined(STM32_TIM1_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM1_UP_NUMBER, STM32_GPT_TIM1_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM1CLK) - gptp->clock = STM32_TIM1CLK; -#else - gptp->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_GPT_USE_TIM2 - if (&GPTD2 == gptp) { - rccEnableTIM2(FALSE); - rccResetTIM2(); -#if !defined(STM32_TIM2_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM2_NUMBER, STM32_GPT_TIM2_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM2CLK) - gptp->clock = STM32_TIM2CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM3 - if (&GPTD3 == gptp) { - rccEnableTIM3(FALSE); - rccResetTIM3(); -#if !defined(STM32_TIM3_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM3_NUMBER, STM32_GPT_TIM3_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM3CLK) - gptp->clock = STM32_TIM3CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM4 - if (&GPTD4 == gptp) { - rccEnableTIM4(FALSE); - rccResetTIM4(); -#if !defined(STM32_TIM4_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM4_NUMBER, STM32_GPT_TIM4_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM4CLK) - gptp->clock = STM32_TIM4CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM5 - if (&GPTD5 == gptp) { - rccEnableTIM5(FALSE); - rccResetTIM5(); -#if !defined(STM32_TIM5_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM5_NUMBER, STM32_GPT_TIM5_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM5CLK) - gptp->clock = STM32_TIM5CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM6 - if (&GPTD6 == gptp) { - rccEnableTIM6(FALSE); - rccResetTIM6(); -#if !defined(STM32_TIM6_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM6_NUMBER, STM32_GPT_TIM6_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM6CLK) - gptp->clock = STM32_TIM6CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM7 - if (&GPTD7 == gptp) { - rccEnableTIM7(FALSE); - rccResetTIM7(); -#if !defined(STM32_TIM7_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM7_NUMBER, STM32_GPT_TIM7_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM7CLK) - gptp->clock = STM32_TIM7CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM8 - if (&GPTD8 == gptp) { - rccEnableTIM8(FALSE); - rccResetTIM8(); -#if !defined(STM32_TIM8_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM8_UP_NUMBER, STM32_GPT_TIM8_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM8CLK) - gptp->clock = STM32_TIM8CLK; -#else - gptp->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_GPT_USE_TIM9 - if (&GPTD9 == gptp) { - rccEnableTIM9(FALSE); - rccResetTIM9(); -#if !defined(STM32_TIM9_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM9_NUMBER, STM32_GPT_TIM9_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM9CLK) - gptp->clock = STM32_TIM9CLK; -#else - gptp->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_GPT_USE_TIM11 - if (&GPTD11 == gptp) { - rccEnableTIM11(FALSE); - rccResetTIM11(); -#if !defined(STM32_TIM11_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM11_NUMBER, STM32_GPT_TIM11_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM11CLK) - gptp->clock = STM32_TIM11CLK; -#else - gptp->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_GPT_USE_TIM12 - if (&GPTD12 == gptp) { - rccEnableTIM12(FALSE); - rccResetTIM12(); -#if !defined(STM32_TIM12_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM12_NUMBER, STM32_GPT_TIM12_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM12CLK) - gptp->clock = STM32_TIM12CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_GPT_USE_TIM14 - if (&GPTD14 == gptp) { - rccEnableTIM14(FALSE); - rccResetTIM14(); -#if !defined(STM32_TIM14_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM14_NUMBER, STM32_GPT_TIM14_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM14CLK) - gptp->clock = STM32_TIM14CLK; -#else - gptp->clock = STM32_TIMCLK1; -#endif - } -#endif - } - - /* Prescaler value calculation.*/ - psc = (uint16_t)((gptp->clock / gptp->config->frequency) - 1); - osalDbgAssert(((uint32_t)(psc + 1) * gptp->config->frequency) == gptp->clock, - "invalid frequency"); - - /* Timer configuration.*/ - gptp->tim->CR1 = 0; /* Initially stopped. */ - gptp->tim->CR2 = gptp->config->cr2; - gptp->tim->PSC = psc; /* Prescaler value. */ - gptp->tim->SR = 0; /* Clear pending IRQs. */ - gptp->tim->DIER = gptp->config->dier & /* DMA-related DIER bits. */ - ~STM32_TIM_DIER_IRQ_MASK; -} - -/** - * @brief Deactivates the GPT peripheral. - * - * @param[in] gptp pointer to the @p GPTDriver object - * - * @notapi - */ -void gpt_lld_stop(GPTDriver *gptp) { - - if (gptp->state == GPT_READY) { - gptp->tim->CR1 = 0; /* Timer disabled. */ - gptp->tim->DIER = 0; /* All IRQs disabled. */ - gptp->tim->SR = 0; /* Clear pending IRQs. */ - -#if STM32_GPT_USE_TIM1 - if (&GPTD1 == gptp) { -#if !defined(STM32_TIM1_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM1_UP_NUMBER); -#endif - rccDisableTIM1(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM2 - if (&GPTD2 == gptp) { -#if !defined(STM32_TIM2_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM2_NUMBER); -#endif - rccDisableTIM2(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM3 - if (&GPTD3 == gptp) { -#if !defined(STM32_TIM3_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM3_NUMBER); -#endif - rccDisableTIM3(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM4 - if (&GPTD4 == gptp) { -#if !defined(STM32_TIM4_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM4_NUMBER); -#endif - rccDisableTIM4(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM5 - if (&GPTD5 == gptp) { -#if !defined(STM32_TIM5_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM5_NUMBER); -#endif - rccDisableTIM5(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM6 - if (&GPTD6 == gptp) { -#if !defined(STM32_TIM6_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM6_NUMBER); -#endif - rccDisableTIM6(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM7 - if (&GPTD7 == gptp) { -#if !defined(STM32_TIM7_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM7_NUMBER); -#endif - rccDisableTIM7(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM8 - if (&GPTD8 == gptp) { -#if !defined(STM32_TIM8_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM8_UP_NUMBER); -#endif - rccDisableTIM8(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM9 - if (&GPTD9 == gptp) { -#if !defined(STM32_TIM9_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM9_NUMBER); -#endif - rccDisableTIM9(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM11 - if (&GPTD11 == gptp) { -#if !defined(STM32_TIM11_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM11_NUMBER); -#endif - rccDisableTIM11(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM12 - if (&GPTD12 == gptp) { -#if !defined(STM32_TIM12_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM12_NUMBER); -#endif - rccDisableTIM12(FALSE); - } -#endif - -#if STM32_GPT_USE_TIM14 - if (&GPTD14 == gptp) { -#if !defined(STM32_TIM14_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM14_NUMBER); -#endif - rccDisableTIM14(FALSE); - } -#endif - } -} - -/** - * @brief Starts the timer in continuous mode. - * - * @param[in] gptp pointer to the @p GPTDriver object - * @param[in] interval period in ticks - * - * @notapi - */ -void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) { - - gptp->tim->ARR = (uint32_t)(interval - 1); /* Time constant. */ - gptp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ - gptp->tim->CNT = 0; /* Reset counter. */ - - /* NOTE: After generating the UG event it takes several clock cycles before - SR bit 0 goes to 1. This is because the clearing of CNT has been inserted - before the clearing of SR, to give it some time.*/ - gptp->tim->SR = 0; /* Clear pending IRQs. */ - if (NULL != gptp->config->callback) - gptp->tim->DIER |= STM32_TIM_DIER_UIE; /* Update Event IRQ enabled.*/ - gptp->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN; -} - -/** - * @brief Stops the timer. - * - * @param[in] gptp pointer to the @p GPTDriver object - * - * @notapi - */ -void gpt_lld_stop_timer(GPTDriver *gptp) { - - gptp->tim->CR1 = 0; /* Initially stopped. */ - gptp->tim->SR = 0; /* Clear pending IRQs. */ - - /* All interrupts disabled.*/ - gptp->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK; -} - -/** - * @brief Starts the timer in one shot mode and waits for completion. - * @details This function specifically polls the timer waiting for completion - * in order to not have extra delays caused by interrupt servicing, - * this function is only recommended for short delays. - * - * @param[in] gptp pointer to the @p GPTDriver object - * @param[in] interval time interval in ticks - * - * @notapi - */ -void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval) { - - gptp->tim->ARR = (uint32_t)(interval - 1); /* Time constant. */ - gptp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ - gptp->tim->CR1 = STM32_TIM_CR1_OPM | STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN; - while (!(gptp->tim->SR & STM32_TIM_SR_UIF)) - ; - gptp->tim->SR = 0; /* Clear pending IRQs. */ -} - -/** - * @brief Shared IRQ handler. - * - * @param[in] gptp pointer to a @p GPTDriver object - * - * @notapi - */ -void gpt_lld_serve_interrupt(GPTDriver *gptp) { - - gptp->tim->SR = 0; - if (gptp->state == GPT_ONESHOT) { - gptp->state = GPT_READY; /* Back in GPT_READY state. */ - gpt_lld_stop_timer(gptp); /* Timer automatically stopped. */ - } - gptp->config->callback(gptp); -} - -#endif /* HAL_USE_GPT */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/gpt_lld.h b/os/hal/ports/STM32/LLD/TIMv1/gpt_lld.h deleted file mode 100644 index 526514a10..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/gpt_lld.h +++ /dev/null @@ -1,641 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/gpt_lld.h - * @brief STM32 GPT subsystem low level driver header. - * - * @addtogroup GPT - * @{ - */ - -#ifndef _GPT_LLD_H_ -#define _GPT_LLD_H_ - -#include "stm32_tim.h" - -#if HAL_USE_GPT || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief GPTD1 driver enable switch. - * @details If set to @p TRUE the support for GPTD1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM1) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM1 FALSE -#endif - -/** - * @brief GPTD2 driver enable switch. - * @details If set to @p TRUE the support for GPTD2 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM2) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM2 FALSE -#endif - -/** - * @brief GPTD3 driver enable switch. - * @details If set to @p TRUE the support for GPTD3 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM3) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM3 FALSE -#endif - -/** - * @brief GPTD4 driver enable switch. - * @details If set to @p TRUE the support for GPTD4 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM4) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM4 FALSE -#endif - -/** - * @brief GPTD5 driver enable switch. - * @details If set to @p TRUE the support for GPTD5 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM5) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM5 FALSE -#endif - -/** - * @brief GPTD6 driver enable switch. - * @details If set to @p TRUE the support for GPTD6 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM6) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM6 FALSE -#endif - -/** - * @brief GPTD7 driver enable switch. - * @details If set to @p TRUE the support for GPTD7 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM7) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM7 FALSE -#endif - -/** - * @brief GPTD8 driver enable switch. - * @details If set to @p TRUE the support for GPTD8 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM8) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM8 FALSE -#endif - -/** - * @brief GPTD9 driver enable switch. - * @details If set to @p TRUE the support for GPTD9 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM9) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM9 FALSE -#endif - -/** - * @brief GPTD11 driver enable switch. - * @details If set to @p TRUE the support for GPTD11 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM11) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM11 FALSE -#endif - -/** - * @brief GPTD12 driver enable switch. - * @details If set to @p TRUE the support for GPTD12 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM12) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM12 FALSE -#endif - -/** - * @brief GPTD14 driver enable switch. - * @details If set to @p TRUE the support for GPTD14 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_GPT_USE_TIM14) || defined(__DOXYGEN__) -#define STM32_GPT_USE_TIM14 FALSE -#endif - -/** - * @brief GPTD1 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM1_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD2 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM2_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD3 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM3_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD4 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM4_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD5 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM5_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD6 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM6_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM6_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD7 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM7_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM7_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD8 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM8_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD9 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM9_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD11 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM11_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM11_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD12 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM12_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM12_IRQ_PRIORITY 7 -#endif - -/** - * @brief GPTD14 interrupt priority level setting. - */ -#if !defined(STM32_GPT_TIM14_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_GPT_TIM14_IRQ_PRIORITY 7 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_GPT_USE_TIM1 && !STM32_HAS_TIM1 -#error "TIM1 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM2 && !STM32_HAS_TIM2 -#error "TIM2 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM3 && !STM32_HAS_TIM3 -#error "TIM3 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM4 && !STM32_HAS_TIM4 -#error "TIM4 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM5 && !STM32_HAS_TIM5 -#error "TIM5 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM6 && !STM32_HAS_TIM6 -#error "TIM6 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM7 && !STM32_HAS_TIM7 -#error "TIM7 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM8 && !STM32_HAS_TIM8 -#error "TIM8 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM9 && !STM32_HAS_TIM9 -#error "TIM9 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM11 && !STM32_HAS_TIM11 -#error "TIM11 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM12 && !STM32_HAS_TIM12 -#error "TIM12 not present in the selected device" -#endif - -#if STM32_GPT_USE_TIM14 && !STM32_HAS_TIM14 -#error "TIM14 not present in the selected device" -#endif - -#if !STM32_GPT_USE_TIM1 && !STM32_GPT_USE_TIM2 && \ - !STM32_GPT_USE_TIM3 && !STM32_GPT_USE_TIM4 && \ - !STM32_GPT_USE_TIM5 && !STM32_GPT_USE_TIM6 && \ - !STM32_GPT_USE_TIM7 && !STM32_GPT_USE_TIM8 && \ - !STM32_GPT_USE_TIM9 && !STM32_GPT_USE_TIM11 && \ - !STM32_GPT_USE_TIM12 && !STM32_GPT_USE_TIM14 -#error "GPT driver activated but no TIM peripheral assigned" -#endif - -/* Checks on allocation of TIMx units.*/ -#if STM32_GPT_USE_TIM1 -#if defined(STM32_TIM1_IS_USED) -#error "GPTD1 requires TIM1 but the timer is already used" -#else -#define STM32_TIM1_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM2 -#if defined(STM32_TIM2_IS_USED) -#error "GPTD2 requires TIM2 but the timer is already used" -#else -#define STM32_TIM2_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM3 -#if defined(STM32_TIM3_IS_USED) -#error "GPTD3 requires TIM3 but the timer is already used" -#else -#define STM32_TIM3_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM4 -#if defined(STM32_TIM4_IS_USED) -#error "GPTD4 requires TIM4 but the timer is already used" -#else -#define STM32_TIM4_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM5 -#if defined(STM32_TIM5_IS_USED) -#error "GPTD5 requires TIM5 but the timer is already used" -#else -#define STM32_TIM5_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM6 -#if defined(STM32_TIM6_IS_USED) -#error "GPTD6 requires TIM6 but the timer is already used" -#else -#define STM32_TIM6_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM7 -#if defined(STM32_TIM7_IS_USED) -#error "GPTD7 requires TIM7 but the timer is already used" -#else -#define STM32_TIM7_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM8 -#if defined(STM32_TIM8_IS_USED) -#error "GPTD8 requires TIM8 but the timer is already used" -#else -#define STM32_TIM8_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM9 -#if defined(STM32_TIM9_IS_USED) -#error "GPTD9 requires TIM9 but the timer is already used" -#else -#define STM32_TIM9_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM11 -#if defined(STM32_TIM11_IS_USED) -#error "GPTD11 requires TIM11 but the timer is already used" -#else -#define STM32_TIM11_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM12 -#if defined(STM32_TIM12_IS_USED) -#error "GPTD12 requires TIM12 but the timer is already used" -#else -#define STM32_TIM12_IS_USED -#endif -#endif - -#if STM32_GPT_USE_TIM14 -#if defined(STM32_TIM14_IS_USED) -#error "GPTD14 requires TIM14 but the timer is already used" -#else -#define STM32_TIM14_IS_USED -#endif -#endif - -/* IRQ priority checks.*/ -#if STM32_GPT_USE_TIM1 && !defined(STM32_TIM1_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM1" -#endif - -#if STM32_GPT_USE_TIM2 && !defined(STM32_TIM2_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM2" -#endif - -#if STM32_GPT_USE_TIM3 && !defined(STM32_TIM3_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM3" -#endif - -#if STM32_GPT_USE_TIM4 && !defined(STM32_TIM_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM4" -#endif - -#if STM32_GPT_USE_TIM5 && !defined(STM32_TIM5_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM5" -#endif - -#if STM32_GPT_USE_TIM6 && !defined(STM32_TIM6_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM6_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM6" -#endif - -#if STM32_GPT_USE_TIM7 && !defined(STM32_TIM7_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM7_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM7" -#endif - -#if STM32_GPT_USE_TIM8 && !defined(STM32_TIM8_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM8_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM8" -#endif - -#if STM32_GPT_USE_TIM9 && !defined(STM32_TIM9_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM9_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM9" -#endif - -#if STM32_GPT_USE_TIM11 && !defined(STM32_TIM11_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM11_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM11" -#endif - -#if STM32_GPT_USE_TIM12 && !defined(STM32_TIM12_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM12_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM12" -#endif - -#if STM32_GPT_USE_TIM14 && !defined(STM32_TIM14_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM14_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM14" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief GPT frequency type. - */ -typedef uint32_t gptfreq_t; - -/** - * @brief GPT counter type. - */ -typedef uint32_t gptcnt_t; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Timer clock in Hz. - * @note The low level can use assertions in order to catch invalid - * frequency specifications. - */ - gptfreq_t frequency; - /** - * @brief Timer callback pointer. - * @note This callback is invoked on GPT counter events. - * @note This callback can be set to @p NULL but in that case the - * one-shot mode cannot be used. - */ - gptcallback_t callback; - /* End of the mandatory fields.*/ - /** - * @brief TIM CR2 register initialization data. - * @note The value of this field should normally be equal to zero. - */ - uint32_t cr2; - /** - * @brief TIM DIER register initialization data. - * @note The value of this field should normally be equal to zero. - * @note Only the DMA-related bits can be specified in this field. - */ - uint32_t dier; -} GPTConfig; - -/** - * @brief Structure representing a GPT driver. - */ -struct GPTDriver { - /** - * @brief Driver state. - */ - gptstate_t state; - /** - * @brief Current configuration data. - */ - const GPTConfig *config; -#if defined(GPT_DRIVER_EXT_FIELDS) - GPT_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Timer base clock. - */ - uint32_t clock; - /** - * @brief Pointer to the TIMx registers block. - */ - stm32_tim_t *tim; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Changes the interval of GPT peripheral. - * @details This function changes the interval of a running GPT unit. - * @pre The GPT unit must be running in continuous mode. - * @post The GPT unit interval is changed to the new value. - * @note The function has effect at the next cycle start. - * - * @param[in] gptp pointer to a @p GPTDriver object - * @param[in] interval new cycle time in timer ticks - * - * @notapi - */ -#define gpt_lld_change_interval(gptp, interval) \ - ((gptp)->tim->ARR = (uint32_t)((interval) - 1)) - -/** - * @brief Returns the interval of GPT peripheral. - * @pre The GPT unit must be running in continuous mode. - * - * @param[in] gptp pointer to a @p GPTDriver object - * @return The current interval. - * - * @notapi - */ -#define gpt_lld_get_interval(gptp) ((gptcnt_t)(gptp)->tim->ARR + 1) - -/** - * @brief Returns the counter value of GPT peripheral. - * @pre The GPT unit must be running in continuous mode. - * @note The nature of the counter is not defined, it may count upward - * or downward, it could be continuously running or not. - * - * @param[in] gptp pointer to a @p GPTDriver object - * @return The current counter value. - * - * @notapi - */ -#define gpt_lld_get_counter(gptp) ((gptcnt_t)(gptp)->tim->CNT) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_GPT_USE_TIM1 && !defined(__DOXYGEN__) -extern GPTDriver GPTD1; -#endif - -#if STM32_GPT_USE_TIM2 && !defined(__DOXYGEN__) -extern GPTDriver GPTD2; -#endif - -#if STM32_GPT_USE_TIM3 && !defined(__DOXYGEN__) -extern GPTDriver GPTD3; -#endif - -#if STM32_GPT_USE_TIM4 && !defined(__DOXYGEN__) -extern GPTDriver GPTD4; -#endif - -#if STM32_GPT_USE_TIM5 && !defined(__DOXYGEN__) -extern GPTDriver GPTD5; -#endif - -#if STM32_GPT_USE_TIM6 && !defined(__DOXYGEN__) -extern GPTDriver GPTD6; -#endif - -#if STM32_GPT_USE_TIM7 && !defined(__DOXYGEN__) -extern GPTDriver GPTD7; -#endif - -#if STM32_GPT_USE_TIM8 && !defined(__DOXYGEN__) -extern GPTDriver GPTD8; -#endif - -#if STM32_GPT_USE_TIM9 && !defined(__DOXYGEN__) -extern GPTDriver GPTD9; -#endif - -#if STM32_GPT_USE_TIM11 && !defined(__DOXYGEN__) -extern GPTDriver GPTD11; -#endif - -#if STM32_GPT_USE_TIM12 && !defined(__DOXYGEN__) -extern GPTDriver GPTD12; -#endif - -#if STM32_GPT_USE_TIM14 && !defined(__DOXYGEN__) -extern GPTDriver GPTD14; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void gpt_lld_init(void); - void gpt_lld_start(GPTDriver *gptp); - void gpt_lld_stop(GPTDriver *gptp); - void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period); - void gpt_lld_stop_timer(GPTDriver *gptp); - void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval); - void gpt_lld_serve_interrupt(GPTDriver *gptp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_GPT */ - -#endif /* _GPT_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.c b/os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.c new file mode 100644 index 000000000..fd2a804c1 --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.c @@ -0,0 +1,891 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/gpt_lld.c + * @brief STM32 GPT subsystem low level driver source. + * + * @addtogroup GPT + * @{ + */ + +#include "hal.h" + +#if HAL_USE_GPT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief GPTD1 driver identifier. + * @note The driver GPTD1 allocates the complex timer TIM1 when enabled. + */ +#if STM32_GPT_USE_TIM1 || defined(__DOXYGEN__) +GPTDriver GPTD1; +#endif + +/** + * @brief GPTD2 driver identifier. + * @note The driver GPTD2 allocates the timer TIM2 when enabled. + */ +#if STM32_GPT_USE_TIM2 || defined(__DOXYGEN__) +GPTDriver GPTD2; +#endif + +/** + * @brief GPTD3 driver identifier. + * @note The driver GPTD3 allocates the timer TIM3 when enabled. + */ +#if STM32_GPT_USE_TIM3 || defined(__DOXYGEN__) +GPTDriver GPTD3; +#endif + +/** + * @brief GPTD4 driver identifier. + * @note The driver GPTD4 allocates the timer TIM4 when enabled. + */ +#if STM32_GPT_USE_TIM4 || defined(__DOXYGEN__) +GPTDriver GPTD4; +#endif + +/** + * @brief GPTD5 driver identifier. + * @note The driver GPTD5 allocates the timer TIM5 when enabled. + */ +#if STM32_GPT_USE_TIM5 || defined(__DOXYGEN__) +GPTDriver GPTD5; +#endif + +/** + * @brief GPTD6 driver identifier. + * @note The driver GPTD6 allocates the timer TIM6 when enabled. + */ +#if STM32_GPT_USE_TIM6 || defined(__DOXYGEN__) +GPTDriver GPTD6; +#endif + +/** + * @brief GPTD7 driver identifier. + * @note The driver GPTD7 allocates the timer TIM7 when enabled. + */ +#if STM32_GPT_USE_TIM7 || defined(__DOXYGEN__) +GPTDriver GPTD7; +#endif + +/** + * @brief GPTD8 driver identifier. + * @note The driver GPTD8 allocates the timer TIM8 when enabled. + */ +#if STM32_GPT_USE_TIM8 || defined(__DOXYGEN__) +GPTDriver GPTD8; +#endif + +/** + * @brief GPTD9 driver identifier. + * @note The driver GPTD9 allocates the timer TIM9 when enabled. + */ +#if STM32_GPT_USE_TIM9 || defined(__DOXYGEN__) +GPTDriver GPTD9; +#endif + +/** + * @brief GPTD11 driver identifier. + * @note The driver GPTD11 allocates the timer TIM11 when enabled. + */ +#if STM32_GPT_USE_TIM11 || defined(__DOXYGEN__) +GPTDriver GPTD11; +#endif + +/** + * @brief GPTD12 driver identifier. + * @note The driver GPTD12 allocates the timer TIM12 when enabled. + */ +#if STM32_GPT_USE_TIM12 || defined(__DOXYGEN__) +GPTDriver GPTD12; +#endif + +/** + * @brief GPTD14 driver identifier. + * @note The driver GPTD14 allocates the timer TIM14 when enabled. + */ +#if STM32_GPT_USE_TIM14 || defined(__DOXYGEN__) +GPTDriver GPTD14; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_GPT_USE_TIM1 || defined(__DOXYGEN__) +#if !defined(STM32_TIM1_SUPPRESS_ISR) +#if !defined(STM32_TIM1_UP_HANDLER) +#error "STM32_TIM1_UP_HANDLER not defined" +#endif +/** + * @brief TIM2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM1_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM1 */ + +#if STM32_GPT_USE_TIM2 || defined(__DOXYGEN__) +#if !defined(STM32_TIM2_SUPPRESS_ISR) +#if !defined(STM32_TIM2_HANDLER) +#error "STM32_TIM2_HANDLER not defined" +#endif +/** + * @brief TIM2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM2_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM2 */ + +#if STM32_GPT_USE_TIM3 || defined(__DOXYGEN__) +#if !defined(STM32_TIM3_SUPPRESS_ISR) +#if !defined(STM32_TIM3_HANDLER) +#error "STM32_TIM3_HANDLER not defined" +#endif +/** + * @brief TIM3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM3_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM3 */ + +#if STM32_GPT_USE_TIM4 || defined(__DOXYGEN__) +#if !defined(STM32_TIM4_SUPPRESS_ISR) +#if !defined(STM32_TIM4_HANDLER) +#error "STM32_TIM4_HANDLER not defined" +#endif +/** + * @brief TIM4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM4_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM4 */ + +#if STM32_GPT_USE_TIM5 || defined(__DOXYGEN__) +#if !defined(STM32_TIM5_SUPPRESS_ISR) +#if !defined(STM32_TIM5_HANDLER) +#error "STM32_TIM5_HANDLER not defined" +#endif +/** + * @brief TIM5 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM5_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM5 */ + +#if STM32_GPT_USE_TIM6 || defined(__DOXYGEN__) +#if !defined(STM32_TIM6_SUPPRESS_ISR) +#if !defined(STM32_TIM6_HANDLER) +#error "STM32_TIM6_HANDLER not defined" +#endif +/** + * @brief TIM6 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM6_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD6); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM6_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM6 */ + +#if STM32_GPT_USE_TIM7 || defined(__DOXYGEN__) +#if !defined(STM32_TIM7_SUPPRESS_ISR) +#if !defined(STM32_TIM7_HANDLER) +#error "STM32_TIM7_HANDLER not defined" +#endif +/** + * @brief TIM7 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM7_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD7); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM7_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM7 */ + +#if STM32_GPT_USE_TIM8 || defined(__DOXYGEN__) +#if !defined(STM32_TIM8_SUPPRESS_ISR) +#if !defined(STM32_TIM8_UP_HANDLER) +#error "STM32_TIM8_UP_HANDLER not defined" +#endif +/** + * @brief TIM8 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD8); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM8_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM8 */ + +#if STM32_GPT_USE_TIM9 || defined(__DOXYGEN__) +#if !defined(STM32_TIM9_SUPPRESS_ISR) +#if !defined(STM32_TIM9_HANDLER) +#error "STM32_TIM9_HANDLER not defined" +#endif +/** + * @brief TIM9 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM9_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD9); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM9_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM9 */ + +#if STM32_GPT_USE_TIM11 || defined(__DOXYGEN__) +#if !defined(STM32_TIM11_SUPPRESS_ISR) +#if !defined(STM32_TIM11_HANDLER) +#error "STM32_TIM11_HANDLER not defined" +#endif +/** + * @brief TIM11 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM11_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD11); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM11_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM11 */ + +#if STM32_GPT_USE_TIM12 || defined(__DOXYGEN__) +#if !defined(STM32_TIM12_SUPPRESS_ISR) +#if !defined(STM32_TIM12_HANDLER) +#error "STM32_TIM12_HANDLER not defined" +#endif +/** + * @brief TIM12 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM12_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD12); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM12_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM12 */ + +#if STM32_GPT_USE_TIM14 || defined(__DOXYGEN__) +#if !defined(STM32_TIM14_SUPPRESS_ISR) +#if !defined(STM32_TIM14_HANDLER) +#error "STM32_TIM14_HANDLER not defined" +#endif +/** + * @brief TIM14 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM14_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + gpt_lld_serve_interrupt(&GPTD14); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM14_SUPPRESS_ISR) */ +#endif /* STM32_GPT_USE_TIM14 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level GPT driver initialization. + * + * @notapi + */ +void gpt_lld_init(void) { + +#if STM32_GPT_USE_TIM1 + /* Driver initialization.*/ + GPTD1.tim = STM32_TIM1; + gptObjectInit(&GPTD1); +#endif + +#if STM32_GPT_USE_TIM2 + /* Driver initialization.*/ + GPTD2.tim = STM32_TIM2; + gptObjectInit(&GPTD2); +#endif + +#if STM32_GPT_USE_TIM3 + /* Driver initialization.*/ + GPTD3.tim = STM32_TIM3; + gptObjectInit(&GPTD3); +#endif + +#if STM32_GPT_USE_TIM4 + /* Driver initialization.*/ + GPTD4.tim = STM32_TIM4; + gptObjectInit(&GPTD4); +#endif + +#if STM32_GPT_USE_TIM5 + /* Driver initialization.*/ + GPTD5.tim = STM32_TIM5; + gptObjectInit(&GPTD5); +#endif + +#if STM32_GPT_USE_TIM6 + /* Driver initialization.*/ + GPTD6.tim = STM32_TIM6; + gptObjectInit(&GPTD6); +#endif + +#if STM32_GPT_USE_TIM7 + /* Driver initialization.*/ + GPTD7.tim = STM32_TIM7; + gptObjectInit(&GPTD7); +#endif + +#if STM32_GPT_USE_TIM8 + /* Driver initialization.*/ + GPTD8.tim = STM32_TIM8; + gptObjectInit(&GPTD8); +#endif + +#if STM32_GPT_USE_TIM9 + /* Driver initialization.*/ + GPTD9.tim = STM32_TIM9; + gptObjectInit(&GPTD9); +#endif + +#if STM32_GPT_USE_TIM11 + /* Driver initialization.*/ + GPTD11.tim = STM32_TIM11; + gptObjectInit(&GPTD11); +#endif + +#if STM32_GPT_USE_TIM12 + /* Driver initialization.*/ + GPTD12.tim = STM32_TIM12; + gptObjectInit(&GPTD12); +#endif + +#if STM32_GPT_USE_TIM14 + /* Driver initialization.*/ + GPTD14.tim = STM32_TIM14; + gptObjectInit(&GPTD14); +#endif +} + +/** + * @brief Configures and activates the GPT peripheral. + * + * @param[in] gptp pointer to the @p GPTDriver object + * + * @notapi + */ +void gpt_lld_start(GPTDriver *gptp) { + uint16_t psc; + + if (gptp->state == GPT_STOP) { + /* Clock activation.*/ +#if STM32_GPT_USE_TIM1 + if (&GPTD1 == gptp) { + rccEnableTIM1(FALSE); + rccResetTIM1(); +#if !defined(STM32_TIM1_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM1_UP_NUMBER, STM32_GPT_TIM1_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM1CLK) + gptp->clock = STM32_TIM1CLK; +#else + gptp->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_GPT_USE_TIM2 + if (&GPTD2 == gptp) { + rccEnableTIM2(FALSE); + rccResetTIM2(); +#if !defined(STM32_TIM2_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM2_NUMBER, STM32_GPT_TIM2_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM2CLK) + gptp->clock = STM32_TIM2CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM3 + if (&GPTD3 == gptp) { + rccEnableTIM3(FALSE); + rccResetTIM3(); +#if !defined(STM32_TIM3_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM3_NUMBER, STM32_GPT_TIM3_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM3CLK) + gptp->clock = STM32_TIM3CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM4 + if (&GPTD4 == gptp) { + rccEnableTIM4(FALSE); + rccResetTIM4(); +#if !defined(STM32_TIM4_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM4_NUMBER, STM32_GPT_TIM4_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM4CLK) + gptp->clock = STM32_TIM4CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM5 + if (&GPTD5 == gptp) { + rccEnableTIM5(FALSE); + rccResetTIM5(); +#if !defined(STM32_TIM5_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM5_NUMBER, STM32_GPT_TIM5_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM5CLK) + gptp->clock = STM32_TIM5CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM6 + if (&GPTD6 == gptp) { + rccEnableTIM6(FALSE); + rccResetTIM6(); +#if !defined(STM32_TIM6_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM6_NUMBER, STM32_GPT_TIM6_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM6CLK) + gptp->clock = STM32_TIM6CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM7 + if (&GPTD7 == gptp) { + rccEnableTIM7(FALSE); + rccResetTIM7(); +#if !defined(STM32_TIM7_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM7_NUMBER, STM32_GPT_TIM7_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM7CLK) + gptp->clock = STM32_TIM7CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM8 + if (&GPTD8 == gptp) { + rccEnableTIM8(FALSE); + rccResetTIM8(); +#if !defined(STM32_TIM8_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM8_UP_NUMBER, STM32_GPT_TIM8_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM8CLK) + gptp->clock = STM32_TIM8CLK; +#else + gptp->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_GPT_USE_TIM9 + if (&GPTD9 == gptp) { + rccEnableTIM9(FALSE); + rccResetTIM9(); +#if !defined(STM32_TIM9_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM9_NUMBER, STM32_GPT_TIM9_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM9CLK) + gptp->clock = STM32_TIM9CLK; +#else + gptp->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_GPT_USE_TIM11 + if (&GPTD11 == gptp) { + rccEnableTIM11(FALSE); + rccResetTIM11(); +#if !defined(STM32_TIM11_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM11_NUMBER, STM32_GPT_TIM11_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM11CLK) + gptp->clock = STM32_TIM11CLK; +#else + gptp->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_GPT_USE_TIM12 + if (&GPTD12 == gptp) { + rccEnableTIM12(FALSE); + rccResetTIM12(); +#if !defined(STM32_TIM12_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM12_NUMBER, STM32_GPT_TIM12_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM12CLK) + gptp->clock = STM32_TIM12CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_GPT_USE_TIM14 + if (&GPTD14 == gptp) { + rccEnableTIM14(FALSE); + rccResetTIM14(); +#if !defined(STM32_TIM14_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM14_NUMBER, STM32_GPT_TIM14_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM14CLK) + gptp->clock = STM32_TIM14CLK; +#else + gptp->clock = STM32_TIMCLK1; +#endif + } +#endif + } + + /* Prescaler value calculation.*/ + psc = (uint16_t)((gptp->clock / gptp->config->frequency) - 1); + osalDbgAssert(((uint32_t)(psc + 1) * gptp->config->frequency) == gptp->clock, + "invalid frequency"); + + /* Timer configuration.*/ + gptp->tim->CR1 = 0; /* Initially stopped. */ + gptp->tim->CR2 = gptp->config->cr2; + gptp->tim->PSC = psc; /* Prescaler value. */ + gptp->tim->SR = 0; /* Clear pending IRQs. */ + gptp->tim->DIER = gptp->config->dier & /* DMA-related DIER bits. */ + ~STM32_TIM_DIER_IRQ_MASK; +} + +/** + * @brief Deactivates the GPT peripheral. + * + * @param[in] gptp pointer to the @p GPTDriver object + * + * @notapi + */ +void gpt_lld_stop(GPTDriver *gptp) { + + if (gptp->state == GPT_READY) { + gptp->tim->CR1 = 0; /* Timer disabled. */ + gptp->tim->DIER = 0; /* All IRQs disabled. */ + gptp->tim->SR = 0; /* Clear pending IRQs. */ + +#if STM32_GPT_USE_TIM1 + if (&GPTD1 == gptp) { +#if !defined(STM32_TIM1_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM1_UP_NUMBER); +#endif + rccDisableTIM1(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM2 + if (&GPTD2 == gptp) { +#if !defined(STM32_TIM2_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM2_NUMBER); +#endif + rccDisableTIM2(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM3 + if (&GPTD3 == gptp) { +#if !defined(STM32_TIM3_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM3_NUMBER); +#endif + rccDisableTIM3(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM4 + if (&GPTD4 == gptp) { +#if !defined(STM32_TIM4_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM4_NUMBER); +#endif + rccDisableTIM4(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM5 + if (&GPTD5 == gptp) { +#if !defined(STM32_TIM5_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM5_NUMBER); +#endif + rccDisableTIM5(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM6 + if (&GPTD6 == gptp) { +#if !defined(STM32_TIM6_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM6_NUMBER); +#endif + rccDisableTIM6(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM7 + if (&GPTD7 == gptp) { +#if !defined(STM32_TIM7_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM7_NUMBER); +#endif + rccDisableTIM7(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM8 + if (&GPTD8 == gptp) { +#if !defined(STM32_TIM8_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM8_UP_NUMBER); +#endif + rccDisableTIM8(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM9 + if (&GPTD9 == gptp) { +#if !defined(STM32_TIM9_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM9_NUMBER); +#endif + rccDisableTIM9(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM11 + if (&GPTD11 == gptp) { +#if !defined(STM32_TIM11_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM11_NUMBER); +#endif + rccDisableTIM11(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM12 + if (&GPTD12 == gptp) { +#if !defined(STM32_TIM12_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM12_NUMBER); +#endif + rccDisableTIM12(FALSE); + } +#endif + +#if STM32_GPT_USE_TIM14 + if (&GPTD14 == gptp) { +#if !defined(STM32_TIM14_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM14_NUMBER); +#endif + rccDisableTIM14(FALSE); + } +#endif + } +} + +/** + * @brief Starts the timer in continuous mode. + * + * @param[in] gptp pointer to the @p GPTDriver object + * @param[in] interval period in ticks + * + * @notapi + */ +void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) { + + gptp->tim->ARR = (uint32_t)(interval - 1); /* Time constant. */ + gptp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ + gptp->tim->CNT = 0; /* Reset counter. */ + + /* NOTE: After generating the UG event it takes several clock cycles before + SR bit 0 goes to 1. This is because the clearing of CNT has been inserted + before the clearing of SR, to give it some time.*/ + gptp->tim->SR = 0; /* Clear pending IRQs. */ + if (NULL != gptp->config->callback) + gptp->tim->DIER |= STM32_TIM_DIER_UIE; /* Update Event IRQ enabled.*/ + gptp->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN; +} + +/** + * @brief Stops the timer. + * + * @param[in] gptp pointer to the @p GPTDriver object + * + * @notapi + */ +void gpt_lld_stop_timer(GPTDriver *gptp) { + + gptp->tim->CR1 = 0; /* Initially stopped. */ + gptp->tim->SR = 0; /* Clear pending IRQs. */ + + /* All interrupts disabled.*/ + gptp->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK; +} + +/** + * @brief Starts the timer in one shot mode and waits for completion. + * @details This function specifically polls the timer waiting for completion + * in order to not have extra delays caused by interrupt servicing, + * this function is only recommended for short delays. + * + * @param[in] gptp pointer to the @p GPTDriver object + * @param[in] interval time interval in ticks + * + * @notapi + */ +void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval) { + + gptp->tim->ARR = (uint32_t)(interval - 1); /* Time constant. */ + gptp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ + gptp->tim->CR1 = STM32_TIM_CR1_OPM | STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN; + while (!(gptp->tim->SR & STM32_TIM_SR_UIF)) + ; + gptp->tim->SR = 0; /* Clear pending IRQs. */ +} + +/** + * @brief Shared IRQ handler. + * + * @param[in] gptp pointer to a @p GPTDriver object + * + * @notapi + */ +void gpt_lld_serve_interrupt(GPTDriver *gptp) { + + gptp->tim->SR = 0; + if (gptp->state == GPT_ONESHOT) { + gptp->state = GPT_READY; /* Back in GPT_READY state. */ + gpt_lld_stop_timer(gptp); /* Timer automatically stopped. */ + } + gptp->config->callback(gptp); +} + +#endif /* HAL_USE_GPT */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.h b/os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.h new file mode 100644 index 000000000..526514a10 --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_gpt_lld.h @@ -0,0 +1,641 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/gpt_lld.h + * @brief STM32 GPT subsystem low level driver header. + * + * @addtogroup GPT + * @{ + */ + +#ifndef _GPT_LLD_H_ +#define _GPT_LLD_H_ + +#include "stm32_tim.h" + +#if HAL_USE_GPT || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief GPTD1 driver enable switch. + * @details If set to @p TRUE the support for GPTD1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM1) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM1 FALSE +#endif + +/** + * @brief GPTD2 driver enable switch. + * @details If set to @p TRUE the support for GPTD2 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM2) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM2 FALSE +#endif + +/** + * @brief GPTD3 driver enable switch. + * @details If set to @p TRUE the support for GPTD3 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM3) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM3 FALSE +#endif + +/** + * @brief GPTD4 driver enable switch. + * @details If set to @p TRUE the support for GPTD4 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM4) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM4 FALSE +#endif + +/** + * @brief GPTD5 driver enable switch. + * @details If set to @p TRUE the support for GPTD5 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM5) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM5 FALSE +#endif + +/** + * @brief GPTD6 driver enable switch. + * @details If set to @p TRUE the support for GPTD6 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM6) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM6 FALSE +#endif + +/** + * @brief GPTD7 driver enable switch. + * @details If set to @p TRUE the support for GPTD7 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM7) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM7 FALSE +#endif + +/** + * @brief GPTD8 driver enable switch. + * @details If set to @p TRUE the support for GPTD8 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM8) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM8 FALSE +#endif + +/** + * @brief GPTD9 driver enable switch. + * @details If set to @p TRUE the support for GPTD9 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM9) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM9 FALSE +#endif + +/** + * @brief GPTD11 driver enable switch. + * @details If set to @p TRUE the support for GPTD11 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM11) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM11 FALSE +#endif + +/** + * @brief GPTD12 driver enable switch. + * @details If set to @p TRUE the support for GPTD12 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM12) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM12 FALSE +#endif + +/** + * @brief GPTD14 driver enable switch. + * @details If set to @p TRUE the support for GPTD14 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_GPT_USE_TIM14) || defined(__DOXYGEN__) +#define STM32_GPT_USE_TIM14 FALSE +#endif + +/** + * @brief GPTD1 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM1_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD2 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM2_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD3 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM3_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD4 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM4_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD5 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM5_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD6 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM6_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM6_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD7 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM7_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM7_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD8 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM8_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD9 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM9_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD11 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM11_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM11_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD12 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM12_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM12_IRQ_PRIORITY 7 +#endif + +/** + * @brief GPTD14 interrupt priority level setting. + */ +#if !defined(STM32_GPT_TIM14_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_GPT_TIM14_IRQ_PRIORITY 7 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_GPT_USE_TIM1 && !STM32_HAS_TIM1 +#error "TIM1 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM2 && !STM32_HAS_TIM2 +#error "TIM2 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM3 && !STM32_HAS_TIM3 +#error "TIM3 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM4 && !STM32_HAS_TIM4 +#error "TIM4 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM5 && !STM32_HAS_TIM5 +#error "TIM5 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM6 && !STM32_HAS_TIM6 +#error "TIM6 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM7 && !STM32_HAS_TIM7 +#error "TIM7 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM8 && !STM32_HAS_TIM8 +#error "TIM8 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM9 && !STM32_HAS_TIM9 +#error "TIM9 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM11 && !STM32_HAS_TIM11 +#error "TIM11 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM12 && !STM32_HAS_TIM12 +#error "TIM12 not present in the selected device" +#endif + +#if STM32_GPT_USE_TIM14 && !STM32_HAS_TIM14 +#error "TIM14 not present in the selected device" +#endif + +#if !STM32_GPT_USE_TIM1 && !STM32_GPT_USE_TIM2 && \ + !STM32_GPT_USE_TIM3 && !STM32_GPT_USE_TIM4 && \ + !STM32_GPT_USE_TIM5 && !STM32_GPT_USE_TIM6 && \ + !STM32_GPT_USE_TIM7 && !STM32_GPT_USE_TIM8 && \ + !STM32_GPT_USE_TIM9 && !STM32_GPT_USE_TIM11 && \ + !STM32_GPT_USE_TIM12 && !STM32_GPT_USE_TIM14 +#error "GPT driver activated but no TIM peripheral assigned" +#endif + +/* Checks on allocation of TIMx units.*/ +#if STM32_GPT_USE_TIM1 +#if defined(STM32_TIM1_IS_USED) +#error "GPTD1 requires TIM1 but the timer is already used" +#else +#define STM32_TIM1_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM2 +#if defined(STM32_TIM2_IS_USED) +#error "GPTD2 requires TIM2 but the timer is already used" +#else +#define STM32_TIM2_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM3 +#if defined(STM32_TIM3_IS_USED) +#error "GPTD3 requires TIM3 but the timer is already used" +#else +#define STM32_TIM3_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM4 +#if defined(STM32_TIM4_IS_USED) +#error "GPTD4 requires TIM4 but the timer is already used" +#else +#define STM32_TIM4_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM5 +#if defined(STM32_TIM5_IS_USED) +#error "GPTD5 requires TIM5 but the timer is already used" +#else +#define STM32_TIM5_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM6 +#if defined(STM32_TIM6_IS_USED) +#error "GPTD6 requires TIM6 but the timer is already used" +#else +#define STM32_TIM6_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM7 +#if defined(STM32_TIM7_IS_USED) +#error "GPTD7 requires TIM7 but the timer is already used" +#else +#define STM32_TIM7_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM8 +#if defined(STM32_TIM8_IS_USED) +#error "GPTD8 requires TIM8 but the timer is already used" +#else +#define STM32_TIM8_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM9 +#if defined(STM32_TIM9_IS_USED) +#error "GPTD9 requires TIM9 but the timer is already used" +#else +#define STM32_TIM9_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM11 +#if defined(STM32_TIM11_IS_USED) +#error "GPTD11 requires TIM11 but the timer is already used" +#else +#define STM32_TIM11_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM12 +#if defined(STM32_TIM12_IS_USED) +#error "GPTD12 requires TIM12 but the timer is already used" +#else +#define STM32_TIM12_IS_USED +#endif +#endif + +#if STM32_GPT_USE_TIM14 +#if defined(STM32_TIM14_IS_USED) +#error "GPTD14 requires TIM14 but the timer is already used" +#else +#define STM32_TIM14_IS_USED +#endif +#endif + +/* IRQ priority checks.*/ +#if STM32_GPT_USE_TIM1 && !defined(STM32_TIM1_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM1" +#endif + +#if STM32_GPT_USE_TIM2 && !defined(STM32_TIM2_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM2" +#endif + +#if STM32_GPT_USE_TIM3 && !defined(STM32_TIM3_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM3" +#endif + +#if STM32_GPT_USE_TIM4 && !defined(STM32_TIM_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM4" +#endif + +#if STM32_GPT_USE_TIM5 && !defined(STM32_TIM5_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM5" +#endif + +#if STM32_GPT_USE_TIM6 && !defined(STM32_TIM6_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM6_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM6" +#endif + +#if STM32_GPT_USE_TIM7 && !defined(STM32_TIM7_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM7_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM7" +#endif + +#if STM32_GPT_USE_TIM8 && !defined(STM32_TIM8_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM8_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM8" +#endif + +#if STM32_GPT_USE_TIM9 && !defined(STM32_TIM9_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM9_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM9" +#endif + +#if STM32_GPT_USE_TIM11 && !defined(STM32_TIM11_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM11_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM11" +#endif + +#if STM32_GPT_USE_TIM12 && !defined(STM32_TIM12_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM12_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM12" +#endif + +#if STM32_GPT_USE_TIM14 && !defined(STM32_TIM14_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_GPT_TIM14_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM14" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief GPT frequency type. + */ +typedef uint32_t gptfreq_t; + +/** + * @brief GPT counter type. + */ +typedef uint32_t gptcnt_t; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + gptfreq_t frequency; + /** + * @brief Timer callback pointer. + * @note This callback is invoked on GPT counter events. + * @note This callback can be set to @p NULL but in that case the + * one-shot mode cannot be used. + */ + gptcallback_t callback; + /* End of the mandatory fields.*/ + /** + * @brief TIM CR2 register initialization data. + * @note The value of this field should normally be equal to zero. + */ + uint32_t cr2; + /** + * @brief TIM DIER register initialization data. + * @note The value of this field should normally be equal to zero. + * @note Only the DMA-related bits can be specified in this field. + */ + uint32_t dier; +} GPTConfig; + +/** + * @brief Structure representing a GPT driver. + */ +struct GPTDriver { + /** + * @brief Driver state. + */ + gptstate_t state; + /** + * @brief Current configuration data. + */ + const GPTConfig *config; +#if defined(GPT_DRIVER_EXT_FIELDS) + GPT_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Timer base clock. + */ + uint32_t clock; + /** + * @brief Pointer to the TIMx registers block. + */ + stm32_tim_t *tim; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Changes the interval of GPT peripheral. + * @details This function changes the interval of a running GPT unit. + * @pre The GPT unit must be running in continuous mode. + * @post The GPT unit interval is changed to the new value. + * @note The function has effect at the next cycle start. + * + * @param[in] gptp pointer to a @p GPTDriver object + * @param[in] interval new cycle time in timer ticks + * + * @notapi + */ +#define gpt_lld_change_interval(gptp, interval) \ + ((gptp)->tim->ARR = (uint32_t)((interval) - 1)) + +/** + * @brief Returns the interval of GPT peripheral. + * @pre The GPT unit must be running in continuous mode. + * + * @param[in] gptp pointer to a @p GPTDriver object + * @return The current interval. + * + * @notapi + */ +#define gpt_lld_get_interval(gptp) ((gptcnt_t)(gptp)->tim->ARR + 1) + +/** + * @brief Returns the counter value of GPT peripheral. + * @pre The GPT unit must be running in continuous mode. + * @note The nature of the counter is not defined, it may count upward + * or downward, it could be continuously running or not. + * + * @param[in] gptp pointer to a @p GPTDriver object + * @return The current counter value. + * + * @notapi + */ +#define gpt_lld_get_counter(gptp) ((gptcnt_t)(gptp)->tim->CNT) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_GPT_USE_TIM1 && !defined(__DOXYGEN__) +extern GPTDriver GPTD1; +#endif + +#if STM32_GPT_USE_TIM2 && !defined(__DOXYGEN__) +extern GPTDriver GPTD2; +#endif + +#if STM32_GPT_USE_TIM3 && !defined(__DOXYGEN__) +extern GPTDriver GPTD3; +#endif + +#if STM32_GPT_USE_TIM4 && !defined(__DOXYGEN__) +extern GPTDriver GPTD4; +#endif + +#if STM32_GPT_USE_TIM5 && !defined(__DOXYGEN__) +extern GPTDriver GPTD5; +#endif + +#if STM32_GPT_USE_TIM6 && !defined(__DOXYGEN__) +extern GPTDriver GPTD6; +#endif + +#if STM32_GPT_USE_TIM7 && !defined(__DOXYGEN__) +extern GPTDriver GPTD7; +#endif + +#if STM32_GPT_USE_TIM8 && !defined(__DOXYGEN__) +extern GPTDriver GPTD8; +#endif + +#if STM32_GPT_USE_TIM9 && !defined(__DOXYGEN__) +extern GPTDriver GPTD9; +#endif + +#if STM32_GPT_USE_TIM11 && !defined(__DOXYGEN__) +extern GPTDriver GPTD11; +#endif + +#if STM32_GPT_USE_TIM12 && !defined(__DOXYGEN__) +extern GPTDriver GPTD12; +#endif + +#if STM32_GPT_USE_TIM14 && !defined(__DOXYGEN__) +extern GPTDriver GPTD14; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void gpt_lld_init(void); + void gpt_lld_start(GPTDriver *gptp); + void gpt_lld_stop(GPTDriver *gptp); + void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period); + void gpt_lld_stop_timer(GPTDriver *gptp); + void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval); + void gpt_lld_serve_interrupt(GPTDriver *gptp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_GPT */ + +#endif /* _GPT_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.c b/os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.c new file mode 100644 index 000000000..f7554241b --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.c @@ -0,0 +1,805 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +/* + Concepts and parts of this file have been contributed by Fabio Utzig and + Xo Wang. + */ + +/** + * @file STM32/icu_lld.c + * @brief STM32 ICU subsystem low level driver header. + * + * @addtogroup ICU + * @{ + */ + +#include "hal.h" + +#if HAL_USE_ICU || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief ICUD1 driver identifier. + * @note The driver ICUD1 allocates the complex timer TIM1 when enabled. + */ +#if STM32_ICU_USE_TIM1 || defined(__DOXYGEN__) +ICUDriver ICUD1; +#endif + +/** + * @brief ICUD2 driver identifier. + * @note The driver ICUD1 allocates the timer TIM2 when enabled. + */ +#if STM32_ICU_USE_TIM2 || defined(__DOXYGEN__) +ICUDriver ICUD2; +#endif + +/** + * @brief ICUD3 driver identifier. + * @note The driver ICUD1 allocates the timer TIM3 when enabled. + */ +#if STM32_ICU_USE_TIM3 || defined(__DOXYGEN__) +ICUDriver ICUD3; +#endif + +/** + * @brief ICUD4 driver identifier. + * @note The driver ICUD4 allocates the timer TIM4 when enabled. + */ +#if STM32_ICU_USE_TIM4 || defined(__DOXYGEN__) +ICUDriver ICUD4; +#endif + +/** + * @brief ICUD5 driver identifier. + * @note The driver ICUD5 allocates the timer TIM5 when enabled. + */ +#if STM32_ICU_USE_TIM5 || defined(__DOXYGEN__) +ICUDriver ICUD5; +#endif + +/** + * @brief ICUD8 driver identifier. + * @note The driver ICUD8 allocates the timer TIM8 when enabled. + */ +#if STM32_ICU_USE_TIM8 || defined(__DOXYGEN__) +ICUDriver ICUD8; +#endif + +/** + * @brief ICUD9 driver identifier. + * @note The driver ICUD9 allocates the timer TIM9 when enabled. + */ +#if STM32_ICU_USE_TIM9 || defined(__DOXYGEN__) +ICUDriver ICUD9; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static bool icu_lld_wait_edge(ICUDriver *icup) { + uint32_t sr; + bool result; + + /* Polled mode so re-enabling the interrupts while the operation is + performed.*/ + osalSysUnlock(); + + /* Polling the right bit depending on the input channel.*/ + if (icup->config->channel == ICU_CHANNEL_1) { + /* Waiting for an edge.*/ + while (((sr = icup->tim->SR) & + (STM32_TIM_SR_CC1IF | STM32_TIM_SR_UIF)) == 0) + ; + } + else { + /* Waiting for an edge.*/ + while (((sr = icup->tim->SR) & + (STM32_TIM_SR_CC2IF | STM32_TIM_SR_UIF)) == 0) + ; + } + + /* Edge or overflow?*/ + result = (sr & STM32_TIM_SR_UIF) != 0 ? true : false; + + /* Done, disabling interrupts again.*/ + osalSysLock(); + + /* Resetting all flags.*/ + icup->tim->SR &= ~(STM32_TIM_SR_CC1IF | + STM32_TIM_SR_CC2IF | + STM32_TIM_SR_UIF); + + return result; +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_ICU_USE_TIM1 || defined(__DOXYGEN__) +#if !defined(STM32_TIM1_SUPPRESS_ISR) +#if !defined(STM32_TIM1_UP_HANDLER) +#error "STM32_TIM1_UP_HANDLER not defined" +#endif +/** + * @brief TIM1 compare interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD1); + + OSAL_IRQ_EPILOGUE(); +} + +#if !defined(STM32_TIM1_CC_HANDLER) +#error "STM32_TIM1_CC_HANDLER not defined" +#endif +/** + * @brief TIM1 compare interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM1_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM1 */ + +#if STM32_ICU_USE_TIM2 || defined(__DOXYGEN__) +#if !defined(STM32_TIM2_SUPPRESS_ISR) +#if !defined(STM32_TIM2_HANDLER) +#error "STM32_TIM2_HANDLER not defined" +#endif +/** + * @brief TIM2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM2_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM2 */ + +#if STM32_ICU_USE_TIM3 || defined(__DOXYGEN__) +#if !defined(STM32_TIM3_SUPPRESS_ISR) +#if !defined(STM32_TIM3_HANDLER) +#error "STM32_TIM3_HANDLER not defined" +#endif +/** + * @brief TIM3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM3_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM3 */ + +#if STM32_ICU_USE_TIM4 || defined(__DOXYGEN__) +#if !defined(STM32_TIM4_SUPPRESS_ISR) +#if !defined(STM32_TIM4_HANDLER) +#error "STM32_TIM4_HANDLER not defined" +#endif +/** + * @brief TIM4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM4_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM4 */ + +#if STM32_ICU_USE_TIM5 || defined(__DOXYGEN__) +#if !defined(STM32_TIM5_SUPPRESS_ISR) +#if !defined(STM32_TIM5_HANDLER) +#error "STM32_TIM5_HANDLER not defined" +#endif +/** + * @brief TIM5 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM5_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM5 */ + +#if STM32_ICU_USE_TIM8 || defined(__DOXYGEN__) +#if !defined(STM32_TIM8_SUPPRESS_ISR) +#if !defined(STM32_TIM8_UP_HANDLER) +#error "STM32_TIM8_UP_HANDLER not defined" +#endif +/** + * @brief TIM8 compare interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD8); + + OSAL_IRQ_EPILOGUE(); +} + +#if !defined(STM32_TIM8_CC_HANDLER) +#error "STM32_TIM8_CC_HANDLER not defined" +#endif +/** + * @brief TIM8 compare interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD8); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM8_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM8 */ + +#if STM32_ICU_USE_TIM9 || defined(__DOXYGEN__) +#if !defined(STM32_TIM9_SUPPRESS_ISR) +#if !defined(STM32_TIM9_HANDLER) +#error "STM32_TIM9_HANDLER not defined" +#endif +/** + * @brief TIM9 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM9_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + icu_lld_serve_interrupt(&ICUD9); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM9_SUPPRESS_ISR) */ +#endif /* STM32_ICU_USE_TIM9 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ICU driver initialization. + * + * @notapi + */ +void icu_lld_init(void) { + +#if STM32_ICU_USE_TIM1 + /* Driver initialization.*/ + icuObjectInit(&ICUD1); + ICUD1.tim = STM32_TIM1; +#endif + +#if STM32_ICU_USE_TIM2 + /* Driver initialization.*/ + icuObjectInit(&ICUD2); + ICUD2.tim = STM32_TIM2; +#endif + +#if STM32_ICU_USE_TIM3 + /* Driver initialization.*/ + icuObjectInit(&ICUD3); + ICUD3.tim = STM32_TIM3; +#endif + +#if STM32_ICU_USE_TIM4 + /* Driver initialization.*/ + icuObjectInit(&ICUD4); + ICUD4.tim = STM32_TIM4; +#endif + +#if STM32_ICU_USE_TIM5 + /* Driver initialization.*/ + icuObjectInit(&ICUD5); + ICUD5.tim = STM32_TIM5; +#endif + +#if STM32_ICU_USE_TIM8 + /* Driver initialization.*/ + icuObjectInit(&ICUD8); + ICUD8.tim = STM32_TIM8; +#endif + +#if STM32_ICU_USE_TIM9 + /* Driver initialization.*/ + icuObjectInit(&ICUD9); + ICUD9.tim = STM32_TIM9; +#endif +} + +/** + * @brief Configures and activates the ICU peripheral. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_start(ICUDriver *icup) { + uint32_t psc; + + osalDbgAssert((icup->config->channel == ICU_CHANNEL_1) || + (icup->config->channel == ICU_CHANNEL_2), + "invalid input"); + + if (icup->state == ICU_STOP) { + /* Clock activation and timer reset.*/ +#if STM32_ICU_USE_TIM1 + if (&ICUD1 == icup) { + rccEnableTIM1(FALSE); + rccResetTIM1(); +#if !defined(STM32_TIM1_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM1_UP_NUMBER, STM32_ICU_TIM1_IRQ_PRIORITY); + nvicEnableVector(STM32_TIM1_CC_NUMBER, STM32_ICU_TIM1_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM1CLK) + icup->clock = STM32_TIM1CLK; +#else + icup->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_ICU_USE_TIM2 + if (&ICUD2 == icup) { + rccEnableTIM2(FALSE); + rccResetTIM2(); +#if !defined(STM32_TIM2_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM2_NUMBER, STM32_ICU_TIM2_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM2CLK) + icup->clock = STM32_TIM2CLK; +#else + icup->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_ICU_USE_TIM3 + if (&ICUD3 == icup) { + rccEnableTIM3(FALSE); + rccResetTIM3(); +#if !defined(STM32_TIM3_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM3_NUMBER, STM32_ICU_TIM3_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM3CLK) + icup->clock = STM32_TIM3CLK; +#else + icup->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_ICU_USE_TIM4 + if (&ICUD4 == icup) { + rccEnableTIM4(FALSE); + rccResetTIM4(); +#if !defined(STM32_TIM4_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM4_NUMBER, STM32_ICU_TIM4_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM4CLK) + icup->clock = STM32_TIM4CLK; +#else + icup->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_ICU_USE_TIM5 + if (&ICUD5 == icup) { + rccEnableTIM5(FALSE); + rccResetTIM5(); +#if !defined(STM32_TIM5_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM5_NUMBER, STM32_ICU_TIM5_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM5CLK) + icup->clock = STM32_TIM5CLK; +#else + icup->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_ICU_USE_TIM8 + if (&ICUD8 == icup) { + rccEnableTIM8(FALSE); + rccResetTIM8(); +#if !defined(STM32_TIM8_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM8_UP_NUMBER, STM32_ICU_TIM8_IRQ_PRIORITY); + nvicEnableVector(STM32_TIM8_CC_NUMBER, STM32_ICU_TIM8_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM8CLK) + icup->clock = STM32_TIM8CLK; +#else + icup->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_ICU_USE_TIM9 + if (&ICUD9 == icup) { + rccEnableTIM9(FALSE); + rccResetTIM9(); +#if !defined(STM32_TIM9_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM9_NUMBER, STM32_ICU_TIM9_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM9CLK) + icup->clock = STM32_TIM9CLK; +#else + icup->clock = STM32_TIMCLK2; +#endif + } +#endif + } + else { + /* Driver re-configuration scenario, it must be stopped first.*/ + icup->tim->CR1 = 0; /* Timer disabled. */ + icup->tim->CCR[0] = 0; /* Comparator 1 disabled. */ + icup->tim->CCR[1] = 0; /* Comparator 2 disabled. */ + icup->tim->CNT = 0; /* Counter reset to zero. */ + } + + /* Timer configuration.*/ + icup->tim->SR = 0; /* Clear eventual pending IRQs. */ + icup->tim->DIER = icup->config->dier & /* DMA-related DIER settings. */ + ~STM32_TIM_DIER_IRQ_MASK; + psc = (icup->clock / icup->config->frequency) - 1; + osalDbgAssert((psc <= 0xFFFF) && + ((psc + 1) * icup->config->frequency) == icup->clock, + "invalid frequency"); + icup->tim->PSC = psc; + icup->tim->ARR = 0xFFFF; + + if (icup->config->channel == ICU_CHANNEL_1) { + /* Selected input 1. + CCMR1_CC1S = 01 = CH1 Input on TI1. + CCMR1_CC2S = 10 = CH2 Input on TI1.*/ + icup->tim->CCMR1 = STM32_TIM_CCMR1_CC1S(1) | STM32_TIM_CCMR1_CC2S(2); + + /* SMCR_TS = 101, input is TI1FP1. + SMCR_SMS = 100, reset on rising edge.*/ + icup->tim->SMCR = STM32_TIM_SMCR_TS(5) | STM32_TIM_SMCR_SMS(4); + + /* The CCER settings depend on the selected trigger mode. + ICU_INPUT_ACTIVE_HIGH: Active on rising edge, idle on falling edge. + ICU_INPUT_ACTIVE_LOW: Active on falling edge, idle on rising edge.*/ + if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH) + icup->tim->CCER = STM32_TIM_CCER_CC1E | + STM32_TIM_CCER_CC2E | STM32_TIM_CCER_CC2P; + else + icup->tim->CCER = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P | + STM32_TIM_CCER_CC2E; + + /* Direct pointers to the capture registers in order to make reading + data faster from within callbacks.*/ + icup->wccrp = &icup->tim->CCR[1]; + icup->pccrp = &icup->tim->CCR[0]; + } + else { + /* Selected input 2. + CCMR1_CC1S = 10 = CH1 Input on TI2. + CCMR1_CC2S = 01 = CH2 Input on TI2.*/ + icup->tim->CCMR1 = STM32_TIM_CCMR1_CC1S(2) | STM32_TIM_CCMR1_CC2S(1); + + /* SMCR_TS = 110, input is TI2FP2. + SMCR_SMS = 100, reset on rising edge.*/ + icup->tim->SMCR = STM32_TIM_SMCR_TS(6) | STM32_TIM_SMCR_SMS(4); + + /* The CCER settings depend on the selected trigger mode. + ICU_INPUT_ACTIVE_HIGH: Active on rising edge, idle on falling edge. + ICU_INPUT_ACTIVE_LOW: Active on falling edge, idle on rising edge.*/ + if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH) + icup->tim->CCER = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P | + STM32_TIM_CCER_CC2E; + else + icup->tim->CCER = STM32_TIM_CCER_CC1E | + STM32_TIM_CCER_CC2E | STM32_TIM_CCER_CC2P; + + /* Direct pointers to the capture registers in order to make reading + data faster from within callbacks.*/ + icup->wccrp = &icup->tim->CCR[0]; + icup->pccrp = &icup->tim->CCR[1]; + } +} + +/** + * @brief Deactivates the ICU peripheral. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_stop(ICUDriver *icup) { + + if (icup->state == ICU_READY) { + /* Clock deactivation.*/ + icup->tim->CR1 = 0; /* Timer disabled. */ + icup->tim->DIER = 0; /* All IRQs disabled. */ + icup->tim->SR = 0; /* Clear eventual pending IRQs. */ + +#if STM32_ICU_USE_TIM1 + if (&ICUD1 == icup) { +#if !defined(STM32_TIM1_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM1_UP_NUMBER); + nvicDisableVector(STM32_TIM1_CC_NUMBER); +#endif + rccDisableTIM1(FALSE); + } +#endif + +#if STM32_ICU_USE_TIM2 + if (&ICUD2 == icup) { +#if !defined(STM32_TIM2_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM2_NUMBER); +#endif + rccDisableTIM2(FALSE); + } +#endif + +#if STM32_ICU_USE_TIM3 + if (&ICUD3 == icup) { +#if !defined(STM32_TIM3_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM3_NUMBER); +#endif + rccDisableTIM3(FALSE); + } +#endif + +#if STM32_ICU_USE_TIM4 + if (&ICUD4 == icup) { +#if !defined(STM32_TIM4_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM4_NUMBER); +#endif + rccDisableTIM4(FALSE); + } +#endif + +#if STM32_ICU_USE_TIM5 + if (&ICUD5 == icup) { +#if !defined(STM32_TIM5_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM5_NUMBER); +#endif + rccDisableTIM5(FALSE); + } +#endif + +#if STM32_ICU_USE_TIM8 + if (&ICUD8 == icup) { +#if !defined(STM32_TIM8_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM8_UP_NUMBER); + nvicDisableVector(STM32_TIM8_CC_NUMBER); +#endif + rccDisableTIM8(FALSE); + } +#endif + +#if STM32_ICU_USE_TIM9 + if (&ICUD9 == icup) { +#if !defined(STM32_TIM9_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM9_NUMBER); +#endif + rccDisableTIM9(FALSE); + } +#endif + } +} + +/** + * @brief Starts the input capture. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_start_capture(ICUDriver *icup) { + + /* Triggering an UG and clearing the IRQ status.*/ + icup->tim->EGR |= STM32_TIM_EGR_UG; + icup->tim->SR = 0; + + /* Timer is started.*/ + icup->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN; +} + +/** + * @brief Waits for a completed capture. + * @note The operation is performed in polled mode. + * @note In order to use this function notifications must be disabled. + * + * @param[in] icup pointer to the @p ICUDriver object + * @return The capture status. + * @retval false if the capture is successful. + * @retval true if a timer overflow occurred. + * + * @notapi + */ +bool icu_lld_wait_capture(ICUDriver *icup) { + + /* If the driver is still in the ICU_WAITING state then we need to wait + for the first activation edge.*/ + if (icup->state == ICU_WAITING) + if (icu_lld_wait_edge(icup)) + return true; + + /* This edge marks the availability of a capture result.*/ + return icu_lld_wait_edge(icup); +} + +/** + * @brief Stops the input capture. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_stop_capture(ICUDriver *icup) { + + /* Timer stopped.*/ + icup->tim->CR1 = 0; + + /* All interrupts disabled.*/ + icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK; +} + +/** + * @brief Enables notifications. + * @pre The ICU unit must have been activated using @p icuStart(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_enable_notifications(ICUDriver *icup) { + uint32_t dier = icup->tim->DIER; + + /* If interrupts were already enabled then the operation is skipped. + This is done in order to avoid clearing the SR and risk losing + pending interrupts.*/ + if ((dier & STM32_TIM_DIER_IRQ_MASK) == 0) { + /* Previously triggered IRQs are ignored, status cleared.*/ + icup->tim->SR = 0; + + if (icup->config->channel == ICU_CHANNEL_1) { + /* Enabling periodic callback on CC1.*/ + dier |= STM32_TIM_DIER_CC1IE; + + /* Optionally enabling width callback on CC2.*/ + if (icup->config->width_cb != NULL) + dier |= STM32_TIM_DIER_CC2IE; + } + else { + /* Enabling periodic callback on CC2.*/ + dier |= STM32_TIM_DIER_CC2IE; + + /* Optionally enabling width callback on CC1.*/ + if (icup->config->width_cb != NULL) + dier |= STM32_TIM_DIER_CC1IE; + } + + /* If an overflow callback is defined then also the overflow callback + is enabled.*/ + if (icup->config->overflow_cb != NULL) + dier |= STM32_TIM_DIER_UIE; + + /* One single atomic write.*/ + icup->tim->DIER = dier; + } +} + +/** + * @brief Disables notifications. + * @pre The ICU unit must have been activated using @p icuStart(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_disable_notifications(ICUDriver *icup) { + + /* All interrupts disabled.*/ + icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK; +} + +/** + * @brief Shared IRQ handler. + * + * @param[in] icup pointer to the @p ICUDriver object + * + * @notapi + */ +void icu_lld_serve_interrupt(ICUDriver *icup) { + uint32_t sr; + + sr = icup->tim->SR; + sr &= icup->tim->DIER & STM32_TIM_DIER_IRQ_MASK; + icup->tim->SR = ~sr; + if (icup->config->channel == ICU_CHANNEL_1) { + if ((sr & STM32_TIM_SR_CC2IF) != 0) + _icu_isr_invoke_width_cb(icup); + if ((sr & STM32_TIM_SR_CC1IF) != 0) + _icu_isr_invoke_period_cb(icup); + } + else { + if ((sr & STM32_TIM_SR_CC1IF) != 0) + _icu_isr_invoke_width_cb(icup); + if ((sr & STM32_TIM_SR_CC2IF) != 0) + _icu_isr_invoke_period_cb(icup); + } + if ((sr & STM32_TIM_SR_UIF) != 0) + _icu_isr_invoke_overflow_cb(icup); +} + +#endif /* HAL_USE_ICU */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.h b/os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.h new file mode 100644 index 000000000..25a4f627a --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_icu_lld.h @@ -0,0 +1,487 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/icu_lld.h + * @brief STM32 ICU subsystem low level driver header. + * + * @addtogroup ICU + * @{ + */ + +#ifndef _ICU_LLD_H_ +#define _ICU_LLD_H_ + +#if HAL_USE_ICU || defined(__DOXYGEN__) + +#include "stm32_tim.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief ICUD1 driver enable switch. + * @details If set to @p TRUE the support for ICUD1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM1) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM1 FALSE +#endif + +/** + * @brief ICUD2 driver enable switch. + * @details If set to @p TRUE the support for ICUD2 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM2) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM2 FALSE +#endif + +/** + * @brief ICUD3 driver enable switch. + * @details If set to @p TRUE the support for ICUD3 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM3) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM3 FALSE +#endif + +/** + * @brief ICUD4 driver enable switch. + * @details If set to @p TRUE the support for ICUD4 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM4) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM4 FALSE +#endif + +/** + * @brief ICUD5 driver enable switch. + * @details If set to @p TRUE the support for ICUD5 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM5) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM5 FALSE +#endif + +/** + * @brief ICUD8 driver enable switch. + * @details If set to @p TRUE the support for ICUD8 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM8) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM8 FALSE +#endif + +/** + * @brief ICUD9 driver enable switch. + * @details If set to @p TRUE the support for ICUD9 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_ICU_USE_TIM9) || defined(__DOXYGEN__) +#define STM32_ICU_USE_TIM9 FALSE +#endif + +/** + * @brief ICUD1 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM1_IRQ_PRIORITY 7 +#endif + +/** + * @brief ICUD2 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM2_IRQ_PRIORITY 7 +#endif + +/** + * @brief ICUD3 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM3_IRQ_PRIORITY 7 +#endif + +/** + * @brief ICUD4 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM4_IRQ_PRIORITY 7 +#endif + +/** + * @brief ICUD5 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM5_IRQ_PRIORITY 7 +#endif + +/** + * @brief ICUD8 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM8_IRQ_PRIORITY 7 +#endif + +/** + * @brief ICUD9 interrupt priority level setting. + */ +#if !defined(STM32_ICU_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ICU_TIM9_IRQ_PRIORITY 7 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_ICU_USE_TIM1 && !STM32_HAS_TIM1 +#error "TIM1 not present in the selected device" +#endif + +#if STM32_ICU_USE_TIM2 && !STM32_HAS_TIM2 +#error "TIM2 not present in the selected device" +#endif + +#if STM32_ICU_USE_TIM3 && !STM32_HAS_TIM3 +#error "TIM3 not present in the selected device" +#endif + +#if STM32_ICU_USE_TIM4 && !STM32_HAS_TIM4 +#error "TIM4 not present in the selected device" +#endif + +#if STM32_ICU_USE_TIM5 && !STM32_HAS_TIM5 +#error "TIM5 not present in the selected device" +#endif + +#if STM32_ICU_USE_TIM8 && !STM32_HAS_TIM8 +#error "TIM8 not present in the selected device" +#endif + +#if STM32_ICU_USE_TIM9 && !STM32_HAS_TIM9 +#error "TIM9 not present in the selected device" +#endif + +#if !STM32_ICU_USE_TIM1 && !STM32_ICU_USE_TIM2 && \ + !STM32_ICU_USE_TIM3 && !STM32_ICU_USE_TIM4 && \ + !STM32_ICU_USE_TIM5 && !STM32_ICU_USE_TIM8 && \ + !STM32_ICU_USE_TIM9 +#error "ICU driver activated but no TIM peripheral assigned" +#endif + +/* Checks on allocation of TIMx units.*/ +#if STM32_ICU_USE_TIM1 +#if defined(STM32_TIM1_IS_USED) +#error "ICUD1 requires TIM1 but the timer is already used" +#else +#define STM32_TIM1_IS_USED +#endif +#endif + +#if STM32_ICU_USE_TIM2 +#if defined(STM32_TIM2_IS_USED) +#error "ICUD2 requires TIM2 but the timer is already used" +#else +#define STM32_TIM2_IS_USED +#endif +#endif + +#if STM32_ICU_USE_TIM3 +#if defined(STM32_TIM3_IS_USED) +#error "ICUD3 requires TIM3 but the timer is already used" +#else +#define STM32_TIM3_IS_USED +#endif +#endif + +#if STM32_ICU_USE_TIM4 +#if defined(STM32_TIM4_IS_USED) +#error "ICUD4 requires TIM4 but the timer is already used" +#else +#define STM32_TIM4_IS_USED +#endif +#endif + +#if STM32_ICU_USE_TIM5 +#if defined(STM32_TIM5_IS_USED) +#error "ICUD5 requires TIM5 but the timer is already used" +#else +#define STM32_TIM5_IS_USED +#endif +#endif + +#if STM32_ICU_USE_TIM8 +#if defined(STM32_TIM8_IS_USED) +#error "ICUD8 requires TIM8 but the timer is already used" +#else +#define STM32_TIM8_IS_USED +#endif +#endif + +#if STM32_ICU_USE_TIM9 +#if defined(STM32_TIM9_IS_USED) +#error "ICUD9 requires TIM9 but the timer is already used" +#else +#define STM32_TIM9_IS_USED +#endif +#endif + +/* IRQ priority checks.*/ +#if STM32_ICU_USE_TIM1 && !defined(STM32_TIM1_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM1" +#endif + +#if STM32_ICU_USE_TIM2 && !defined(STM32_TIM2_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM2" +#endif + +#if STM32_ICU_USE_TIM3 && !defined(STM32_TIM3_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM3" +#endif + +#if STM32_ICU_USE_TIM4 && !defined(STM32_TIM4_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM4" +#endif + +#if STM32_ICU_USE_TIM5 && !defined(STM32_TIM5_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM5" +#endif + +#if STM32_ICU_USE_TIM8 && !defined(STM32_TIM8_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM8_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM8" +#endif + +#if STM32_ICU_USE_TIM9 && !defined(STM32_TIM9_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM9_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM9" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief ICU driver mode. + */ +typedef enum { + ICU_INPUT_ACTIVE_HIGH = 0, /**< Trigger on rising edge. */ + ICU_INPUT_ACTIVE_LOW = 1, /**< Trigger on falling edge. */ +} icumode_t; + +/** + * @brief ICU frequency type. + */ +typedef uint32_t icufreq_t; + +/** + * @brief ICU channel type. + */ +typedef enum { + ICU_CHANNEL_1 = 0, /**< Use TIMxCH1. */ + ICU_CHANNEL_2 = 1, /**< Use TIMxCH2. */ +} icuchannel_t; + +/** + * @brief ICU counter type. + */ +typedef uint32_t icucnt_t; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Driver mode. + */ + icumode_t mode; + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + icufreq_t frequency; + /** + * @brief Callback for pulse width measurement. + */ + icucallback_t width_cb; + /** + * @brief Callback for cycle period measurement. + */ + icucallback_t period_cb; + /** + * @brief Callback for timer overflow. + */ + icucallback_t overflow_cb; + /* End of the mandatory fields.*/ + /** + * @brief Timer input channel to be used. + * @note Only inputs TIMx 1 and 2 are supported. + */ + icuchannel_t channel; + /** + * @brief TIM DIER register initialization data. + * @note The value of this field should normally be equal to zero. + * @note Only the DMA-related bits can be specified in this field. + */ + uint32_t dier; +} ICUConfig; + +/** + * @brief Structure representing an ICU driver. + */ +struct ICUDriver { + /** + * @brief Driver state. + */ + icustate_t state; + /** + * @brief Current configuration data. + */ + const ICUConfig *config; +#if defined(ICU_DRIVER_EXT_FIELDS) + ICU_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Timer base clock. + */ + uint32_t clock; + /** + * @brief Pointer to the TIMx registers block. + */ + stm32_tim_t *tim; + /** + * @brief CCR register used for width capture. + */ + volatile uint32_t *wccrp; + /** + * @brief CCR register used for period capture. + */ + volatile uint32_t *pccrp; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the width of the latest pulse. + * @details The pulse width is defined as number of ticks between the start + * edge and the stop edge. + * + * @param[in] icup pointer to the @p ICUDriver object + * @return The number of ticks. + * + * @notapi + */ +#define icu_lld_get_width(icup) (*((icup)->wccrp) + 1) + +/** + * @brief Returns the width of the latest cycle. + * @details The cycle width is defined as number of ticks between a start + * edge and the next start edge. + * + * @param[in] icup pointer to the @p ICUDriver object + * @return The number of ticks. + * + * @notapi + */ +#define icu_lld_get_period(icup) (*((icup)->pccrp) + 1) + +/** + * @brief Check on notifications status. + * + * @param[in] icup pointer to the @p ICUDriver object + * @return The notifications status. + * @retval false if notifications are not enabled. + * @retval true if notifications are enabled. + * + * @notapi + */ +#define icu_lld_are_notifications_enabled(icup) \ + (bool)(((icup)->tim->DIER & STM32_TIM_DIER_IRQ_MASK) != 0) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_ICU_USE_TIM1 && !defined(__DOXYGEN__) +extern ICUDriver ICUD1; +#endif + +#if STM32_ICU_USE_TIM2 && !defined(__DOXYGEN__) +extern ICUDriver ICUD2; +#endif + +#if STM32_ICU_USE_TIM3 && !defined(__DOXYGEN__) +extern ICUDriver ICUD3; +#endif + +#if STM32_ICU_USE_TIM4 && !defined(__DOXYGEN__) +extern ICUDriver ICUD4; +#endif + +#if STM32_ICU_USE_TIM5 && !defined(__DOXYGEN__) +extern ICUDriver ICUD5; +#endif + +#if STM32_ICU_USE_TIM8 && !defined(__DOXYGEN__) +extern ICUDriver ICUD8; +#endif + +#if STM32_ICU_USE_TIM9 && !defined(__DOXYGEN__) +extern ICUDriver ICUD9; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void icu_lld_init(void); + void icu_lld_start(ICUDriver *icup); + void icu_lld_stop(ICUDriver *icup); + void icu_lld_start_capture(ICUDriver *icup); + bool icu_lld_wait_capture(ICUDriver *icup); + void icu_lld_stop_capture(ICUDriver *icup); + void icu_lld_enable_notifications(ICUDriver *icup); + void icu_lld_disable_notifications(ICUDriver *icup); + void icu_lld_serve_interrupt(ICUDriver *icup); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_ICU */ + +#endif /* _ICU_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.c b/os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.c new file mode 100644 index 000000000..7837b4d02 --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.c @@ -0,0 +1,858 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/pwm_lld.c + * @brief STM32 PWM subsystem low level driver header. + * + * @addtogroup PWM + * @{ + */ + +#include "hal.h" + +#if HAL_USE_PWM || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief PWMD1 driver identifier. + * @note The driver PWMD1 allocates the complex timer TIM1 when enabled. + */ +#if STM32_PWM_USE_TIM1 || defined(__DOXYGEN__) +PWMDriver PWMD1; +#endif + +/** + * @brief PWMD2 driver identifier. + * @note The driver PWMD2 allocates the timer TIM2 when enabled. + */ +#if STM32_PWM_USE_TIM2 || defined(__DOXYGEN__) +PWMDriver PWMD2; +#endif + +/** + * @brief PWMD3 driver identifier. + * @note The driver PWMD3 allocates the timer TIM3 when enabled. + */ +#if STM32_PWM_USE_TIM3 || defined(__DOXYGEN__) +PWMDriver PWMD3; +#endif + +/** + * @brief PWMD4 driver identifier. + * @note The driver PWMD4 allocates the timer TIM4 when enabled. + */ +#if STM32_PWM_USE_TIM4 || defined(__DOXYGEN__) +PWMDriver PWMD4; +#endif + +/** + * @brief PWMD5 driver identifier. + * @note The driver PWMD5 allocates the timer TIM5 when enabled. + */ +#if STM32_PWM_USE_TIM5 || defined(__DOXYGEN__) +PWMDriver PWMD5; +#endif + +/** + * @brief PWMD8 driver identifier. + * @note The driver PWMD8 allocates the timer TIM8 when enabled. + */ +#if STM32_PWM_USE_TIM8 || defined(__DOXYGEN__) +PWMDriver PWMD8; +#endif + +/** + * @brief PWMD9 driver identifier. + * @note The driver PWMD9 allocates the timer TIM9 when enabled. + */ +#if STM32_PWM_USE_TIM9 || defined(__DOXYGEN__) +PWMDriver PWMD9; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_PWM_USE_TIM1 || defined(__DOXYGEN__) +#if !defined(STM32_TIM1_SUPPRESS_ISR) +#if !defined(STM32_TIM1_UP_HANDLER) +#error "STM32_TIM1_UP_HANDLER not defined" +#endif +/** + * @brief TIM1 update interrupt handler. + * @note It is assumed that this interrupt is only activated if the callback + * pointer is not equal to @p NULL in order to not perform an extra + * check in a potentially critical interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD1); + + OSAL_IRQ_EPILOGUE(); +} + +#if !defined(STM32_TIM1_CC_HANDLER) +#error "STM32_TIM1_CC_HANDLER not defined" +#endif +/** + * @brief TIM1 compare interrupt handler. + * @note It is assumed that the various sources are only activated if the + * associated callback pointer is not equal to @p NULL in order to not + * perform an extra check in a potentially critical interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM1_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM1 */ + +#if STM32_PWM_USE_TIM2 || defined(__DOXYGEN__) +#if !defined(STM32_TIM2_SUPPRESS_ISR) +#if !defined(STM32_TIM2_HANDLER) +#error "STM32_TIM2_HANDLER not defined" +#endif +/** + * @brief TIM2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM2_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM2 */ + +#if STM32_PWM_USE_TIM3 || defined(__DOXYGEN__) +#if !defined(STM32_TIM3_SUPPRESS_ISR) +#if !defined(STM32_TIM3_HANDLER) +#error "STM32_TIM3_HANDLER not defined" +#endif +/** + * @brief TIM3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM3_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM3 */ + +#if STM32_PWM_USE_TIM4 || defined(__DOXYGEN__) +#if !defined(STM32_TIM4_SUPPRESS_ISR) +#if !defined(STM32_TIM4_HANDLER) +#error "STM32_TIM4_HANDLER not defined" +#endif +/** + * @brief TIM4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM4_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM4 */ + +#if STM32_PWM_USE_TIM5 || defined(__DOXYGEN__) +#if !defined(STM32_TIM5_SUPPRESS_ISR) +#if !defined(STM32_TIM5_HANDLER) +#error "STM32_TIM5_HANDLER not defined" +#endif +/** + * @brief TIM5 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM5_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM5 */ + +#if STM32_PWM_USE_TIM8 || defined(__DOXYGEN__) +#if !defined(STM32_TIM8_SUPPRESS_ISR) +#if !defined(STM32_TIM8_UP_HANDLER) +#error "STM32_TIM8_UP_HANDLER not defined" +#endif +/** + * @brief TIM8 update interrupt handler. + * @note It is assumed that this interrupt is only activated if the callback + * pointer is not equal to @p NULL in order to not perform an extra + * check in a potentially critical interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD8); + + OSAL_IRQ_EPILOGUE(); +} + +#if !defined(STM32_TIM8_CC_HANDLER) +#error "STM32_TIM8_CC_HANDLER not defined" +#endif +/** + * @brief TIM8 compare interrupt handler. + * @note It is assumed that the various sources are only activated if the + * associated callback pointer is not equal to @p NULL in order to not + * perform an extra check in a potentially critical interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD8); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM8_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM8 */ + +#if STM32_PWM_USE_TIM9 || defined(__DOXYGEN__) +#if !defined(STM32_TIM9_SUPPRESS_ISR) +#if !defined(STM32_TIM9_HANDLER) +#error "STM32_TIM9_HANDLER not defined" +#endif +/** + * @brief TIM9 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_TIM9_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + pwm_lld_serve_interrupt(&PWMD9); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* !defined(STM32_TIM9_SUPPRESS_ISR) */ +#endif /* STM32_PWM_USE_TIM9 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level PWM driver initialization. + * + * @notapi + */ +void pwm_lld_init(void) { + +#if STM32_PWM_USE_TIM1 + /* Driver initialization.*/ + pwmObjectInit(&PWMD1); + PWMD1.channels = STM32_TIM1_CHANNELS; + PWMD1.tim = STM32_TIM1; +#endif + +#if STM32_PWM_USE_TIM2 + /* Driver initialization.*/ + pwmObjectInit(&PWMD2); + PWMD2.channels = STM32_TIM2_CHANNELS; + PWMD2.tim = STM32_TIM2; +#endif + +#if STM32_PWM_USE_TIM3 + /* Driver initialization.*/ + pwmObjectInit(&PWMD3); + PWMD3.channels = STM32_TIM3_CHANNELS; + PWMD3.tim = STM32_TIM3; +#endif + +#if STM32_PWM_USE_TIM4 + /* Driver initialization.*/ + pwmObjectInit(&PWMD4); + PWMD4.channels = STM32_TIM4_CHANNELS; + PWMD4.tim = STM32_TIM4; +#endif + +#if STM32_PWM_USE_TIM5 + /* Driver initialization.*/ + pwmObjectInit(&PWMD5); + PWMD5.channels = STM32_TIM5_CHANNELS; + PWMD5.tim = STM32_TIM5; +#endif + +#if STM32_PWM_USE_TIM8 + /* Driver initialization.*/ + pwmObjectInit(&PWMD8); + PWMD8.channels = STM32_TIM8_CHANNELS; + PWMD8.tim = STM32_TIM8; +#endif + +#if STM32_PWM_USE_TIM9 + /* Driver initialization.*/ + pwmObjectInit(&PWMD9); + PWMD9.channels = STM32_TIM9_CHANNELS; + PWMD9.tim = STM32_TIM9; +#endif +} + +/** + * @brief Configures and activates the PWM peripheral. + * @note Starting a driver that is already in the @p PWM_READY state + * disables all the active channels. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_start(PWMDriver *pwmp) { + uint32_t psc; + uint32_t ccer; + + if (pwmp->state == PWM_STOP) { + /* Clock activation and timer reset.*/ +#if STM32_PWM_USE_TIM1 + if (&PWMD1 == pwmp) { + rccEnableTIM1(FALSE); + rccResetTIM1(); +#if !defined(STM32_TIM1_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM1_UP_NUMBER, STM32_PWM_TIM1_IRQ_PRIORITY); + nvicEnableVector(STM32_TIM1_CC_NUMBER, STM32_PWM_TIM1_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM1CLK) + pwmp->clock = STM32_TIM1CLK; +#else + pwmp->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_PWM_USE_TIM2 + if (&PWMD2 == pwmp) { + rccEnableTIM2(FALSE); + rccResetTIM2(); +#if !defined(STM32_TIM2_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM2_NUMBER, STM32_PWM_TIM2_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM2CLK) + pwmp->clock = STM32_TIM2CLK; +#else + pwmp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_PWM_USE_TIM3 + if (&PWMD3 == pwmp) { + rccEnableTIM3(FALSE); + rccResetTIM3(); +#if !defined(STM32_TIM3_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM3_NUMBER, STM32_PWM_TIM3_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM3CLK) + pwmp->clock = STM32_TIM3CLK; +#else + pwmp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_PWM_USE_TIM4 + if (&PWMD4 == pwmp) { + rccEnableTIM4(FALSE); + rccResetTIM4(); +#if !defined(STM32_TIM4_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM4_NUMBER, STM32_PWM_TIM4_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM4CLK) + pwmp->clock = STM32_TIM4CLK; +#else + pwmp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_PWM_USE_TIM5 + if (&PWMD5 == pwmp) { + rccEnableTIM5(FALSE); + rccResetTIM5(); +#if !defined(STM32_TIM5_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM5_NUMBER, STM32_PWM_TIM5_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM5CLK) + pwmp->clock = STM32_TIM5CLK; +#else + pwmp->clock = STM32_TIMCLK1; +#endif + } +#endif + +#if STM32_PWM_USE_TIM8 + if (&PWMD8 == pwmp) { + rccEnableTIM8(FALSE); + rccResetTIM8(); +#if !defined(STM32_TIM8_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM8_UP_NUMBER, STM32_PWM_TIM8_IRQ_PRIORITY); + nvicEnableVector(STM32_TIM8_CC_NUMBER, STM32_PWM_TIM8_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM8CLK) + pwmp->clock = STM32_TIM8CLK; +#else + pwmp->clock = STM32_TIMCLK2; +#endif + } +#endif + +#if STM32_PWM_USE_TIM9 + if (&PWMD9 == pwmp) { + rccEnableTIM9(FALSE); + rccResetTIM9(); +#if !defined(STM32_TIM9_SUPPRESS_ISR) + nvicEnableVector(STM32_TIM9_NUMBER, STM32_PWM_TIM9_IRQ_PRIORITY); +#endif +#if defined(STM32_TIM9CLK) + pwmp->clock = STM32_TIM9CLK; +#else + pwmp->clock = STM32_TIMCLK2; +#endif + } +#endif + + /* All channels configured in PWM1 mode with preload enabled and will + stay that way until the driver is stopped.*/ + pwmp->tim->CCMR1 = STM32_TIM_CCMR1_OC1M(6) | STM32_TIM_CCMR1_OC1PE | + STM32_TIM_CCMR1_OC2M(6) | STM32_TIM_CCMR1_OC2PE; + pwmp->tim->CCMR2 = STM32_TIM_CCMR2_OC3M(6) | STM32_TIM_CCMR2_OC3PE | + STM32_TIM_CCMR2_OC4M(6) | STM32_TIM_CCMR2_OC4PE; +#if STM32_TIM_MAX_CHANNELS > 4 + pwmp->tim->CCMR3 = STM32_TIM_CCMR3_OC5M(6) | STM32_TIM_CCMR3_OC5PE | + STM32_TIM_CCMR3_OC6M(6) | STM32_TIM_CCMR3_OC6PE; +#endif + } + else { + /* Driver re-configuration scenario, it must be stopped first.*/ + pwmp->tim->CR1 = 0; /* Timer disabled. */ + pwmp->tim->CCR[0] = 0; /* Comparator 1 disabled. */ + pwmp->tim->CCR[1] = 0; /* Comparator 2 disabled. */ + pwmp->tim->CCR[2] = 0; /* Comparator 3 disabled. */ + pwmp->tim->CCR[3] = 0; /* Comparator 4 disabled. */ +#if STM32_TIM_MAX_CHANNELS > 4 + if (pwmp->channels > 4) { + pwmp->tim->CCXR[0] = 0; /* Comparator 5 disabled. */ + pwmp->tim->CCXR[1] = 0; /* Comparator 6 disabled. */ + } +#endif + pwmp->tim->CNT = 0; /* Counter reset to zero. */ + } + + /* Timer configuration.*/ + psc = (pwmp->clock / pwmp->config->frequency) - 1; + osalDbgAssert((psc <= 0xFFFF) && + ((psc + 1) * pwmp->config->frequency) == pwmp->clock, + "invalid frequency"); + pwmp->tim->PSC = psc; + pwmp->tim->ARR = pwmp->period - 1; + pwmp->tim->CR2 = pwmp->config->cr2; + + /* Output enables and polarities setup.*/ + ccer = 0; + switch (pwmp->config->channels[0].mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC1P; + case PWM_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC1E; + default: + ; + } + switch (pwmp->config->channels[1].mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC2P; + case PWM_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC2E; + default: + ; + } + switch (pwmp->config->channels[2].mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC3P; + case PWM_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC3E; + default: + ; + } + switch (pwmp->config->channels[3].mode & PWM_OUTPUT_MASK) { + case PWM_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC4P; + case PWM_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC4E; + default: + ; + } +#if STM32_PWM_USE_ADVANCED +#if STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM8 + if (&PWMD1 == pwmp) { +#endif +#if !STM32_PWM_USE_TIM1 && STM32_PWM_USE_TIM8 + if (&PWMD8 == pwmp) { +#endif +#if STM32_PWM_USE_TIM1 && STM32_PWM_USE_TIM8 + if ((&PWMD1 == pwmp) || (&PWMD8 == pwmp)) { +#endif + switch (pwmp->config->channels[0].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { + case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC1NP; + case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC1NE; + default: + ; + } + switch (pwmp->config->channels[1].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { + case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC2NP; + case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC2NE; + default: + ; + } + switch (pwmp->config->channels[2].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { + case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: + ccer |= STM32_TIM_CCER_CC3NP; + case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: + ccer |= STM32_TIM_CCER_CC3NE; + default: + ; + } + } +#endif /* STM32_PWM_USE_ADVANCED*/ + + pwmp->tim->CCER = ccer; + pwmp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ + pwmp->tim->SR = 0; /* Clear pending IRQs. */ + pwmp->tim->DIER = pwmp->config->dier & /* DMA-related DIER settings. */ + ~STM32_TIM_DIER_IRQ_MASK; +#if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8 +#if STM32_PWM_USE_ADVANCED + pwmp->tim->BDTR = pwmp->config->bdtr | STM32_TIM_BDTR_MOE; +#else + pwmp->tim->BDTR = STM32_TIM_BDTR_MOE; +#endif +#endif + /* Timer configured and started.*/ + pwmp->tim->CR1 = STM32_TIM_CR1_ARPE | STM32_TIM_CR1_URS | + STM32_TIM_CR1_CEN; +} + +/** + * @brief Deactivates the PWM peripheral. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_stop(PWMDriver *pwmp) { + + /* If in ready state then disables the PWM clock.*/ + if (pwmp->state == PWM_READY) { + pwmp->tim->CR1 = 0; /* Timer disabled. */ + pwmp->tim->DIER = 0; /* All IRQs disabled. */ + pwmp->tim->SR = 0; /* Clear eventual pending IRQs. */ +#if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8 + pwmp->tim->BDTR = 0; +#endif + +#if STM32_PWM_USE_TIM1 + if (&PWMD1 == pwmp) { +#if !defined(STM32_TIM1_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM1_UP_NUMBER); + nvicDisableVector(STM32_TIM1_CC_NUMBER); +#endif + rccDisableTIM1(FALSE); + } +#endif + +#if STM32_PWM_USE_TIM2 + if (&PWMD2 == pwmp) { +#if !defined(STM32_TIM2_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM2_NUMBER); +#endif + rccDisableTIM2(FALSE); + } +#endif + +#if STM32_PWM_USE_TIM3 + if (&PWMD3 == pwmp) { +#if !defined(STM32_TIM3_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM3_NUMBER); +#endif + rccDisableTIM3(FALSE); + } +#endif + +#if STM32_PWM_USE_TIM4 + if (&PWMD4 == pwmp) { +#if !defined(STM32_TIM4_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM4_NUMBER); +#endif + rccDisableTIM4(FALSE); + } +#endif + +#if STM32_PWM_USE_TIM5 + if (&PWMD5 == pwmp) { +#if !defined(STM32_TIM5_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM5_NUMBER); +#endif + rccDisableTIM5(FALSE); + } +#endif + +#if STM32_PWM_USE_TIM8 + if (&PWMD8 == pwmp) { +#if !defined(STM32_TIM8_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM8_UP_NUMBER); + nvicDisableVector(STM32_TIM8_CC_NUMBER); +#endif + rccDisableTIM8(FALSE); + } +#endif + +#if STM32_PWM_USE_TIM9 + if (&PWMD9 == pwmp) { +#if !defined(STM32_TIM9_SUPPRESS_ISR) + nvicDisableVector(STM32_TIM9_NUMBER); +#endif + rccDisableTIM9(FALSE); + } +#endif + } +} + +/** + * @brief Enables a PWM channel. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The channel is active using the specified configuration. + * @note The function has effect at the next cycle start. + * @note Channel notification is not enabled. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * @param[in] width PWM pulse width as clock pulses number + * + * @notapi + */ +void pwm_lld_enable_channel(PWMDriver *pwmp, + pwmchannel_t channel, + pwmcnt_t width) { + + /* Changing channel duty cycle on the fly.*/ +#if STM32_TIM_MAX_CHANNELS <= 4 + pwmp->tim->CCR[channel] = width; +#else + if (channel < 4) + pwmp->tim->CCR[channel] = width; + else + pwmp->tim->CCXR[channel - 4] = width; +#endif +} + +/** + * @brief Disables a PWM channel and its notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The channel is disabled and its output line returned to the + * idle state. + * @note The function has effect at the next cycle start. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @notapi + */ +void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) { + +#if STM32_TIM_MAX_CHANNELS <= 4 + pwmp->tim->CCR[channel] = 0; + pwmp->tim->DIER &= ~(2 << channel); +#else + if (channel < 4) { + pwmp->tim->CCR[channel] = 0; + pwmp->tim->DIER &= ~(2 << channel); + } + else + pwmp->tim->CCXR[channel - 4] = 0; +#endif +} + +/** + * @brief Enables the periodic activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_enable_periodic_notification(PWMDriver *pwmp) { + uint32_t dier = pwmp->tim->DIER; + + /* If the IRQ is not already enabled care must be taken to clear it, + it is probably already pending because the timer is running.*/ + if ((dier & STM32_TIM_DIER_UIE) == 0) { + pwmp->tim->DIER = dier | STM32_TIM_DIER_UIE; + pwmp->tim->SR &= STM32_TIM_SR_UIF; + } +} + +/** + * @brief Disables the periodic activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_disable_periodic_notification(PWMDriver *pwmp) { + + pwmp->tim->DIER &= ~STM32_TIM_DIER_UIE; +} + +/** + * @brief Enables a channel de-activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @pre The channel must have been activated using @p pwmEnableChannel(). + * @note If the notification is already enabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @notapi + */ +void pwm_lld_enable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel) { + uint32_t dier = pwmp->tim->DIER; + +#if STM32_TIM_MAX_CHANNELS > 4 + /* Channels 4 and 5 do not support callbacks.*/ + osalDbgAssert(channel < 4, "callback not supported"); +#endif + + /* If the IRQ is not already enabled care must be taken to clear it, + it is probably already pending because the timer is running.*/ + if ((dier & (2 << channel)) == 0) { + pwmp->tim->DIER = dier | (2 << channel); + pwmp->tim->SR = ~(2 << channel); + } +} + +/** + * @brief Disables a channel de-activation edge notification. + * @pre The PWM unit must have been activated using @p pwmStart(). + * @pre The channel must have been activated using @p pwmEnableChannel(). + * @note If the notification is already disabled then the call has no effect. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] channel PWM channel identifier (0...channels-1) + * + * @notapi + */ +void pwm_lld_disable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel) { + + pwmp->tim->DIER &= ~(2 << channel); +} + +/** + * @brief Common TIM2...TIM5,TIM9 IRQ handler. + * @note It is assumed that the various sources are only activated if the + * associated callback pointer is not equal to @p NULL in order to not + * perform an extra check in a potentially critical interrupt handler. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * + * @notapi + */ +void pwm_lld_serve_interrupt(PWMDriver *pwmp) { + uint32_t sr; + + sr = pwmp->tim->SR; + sr &= pwmp->tim->DIER & STM32_TIM_DIER_IRQ_MASK; + pwmp->tim->SR = ~sr; + if (((sr & STM32_TIM_SR_CC1IF) != 0) && + (pwmp->config->channels[0].callback != NULL)) + pwmp->config->channels[0].callback(pwmp); + if (((sr & STM32_TIM_SR_CC2IF) != 0) && + (pwmp->config->channels[1].callback != NULL)) + pwmp->config->channels[1].callback(pwmp); + if (((sr & STM32_TIM_SR_CC3IF) != 0) && + (pwmp->config->channels[2].callback != NULL)) + pwmp->config->channels[2].callback(pwmp); + if (((sr & STM32_TIM_SR_CC4IF) != 0) && + (pwmp->config->channels[3].callback != NULL)) + pwmp->config->channels[3].callback(pwmp); + if (((sr & STM32_TIM_SR_UIF) != 0) && (pwmp->config->callback != NULL)) + pwmp->config->callback(pwmp); +} + +#endif /* HAL_USE_PWM */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.h b/os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.h new file mode 100644 index 000000000..981312f38 --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_pwm_lld.h @@ -0,0 +1,550 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/pwm_lld.h + * @brief STM32 PWM subsystem low level driver header. + * + * @addtogroup PWM + * @{ + */ + +#ifndef _PWM_LLD_H_ +#define _PWM_LLD_H_ + +#if HAL_USE_PWM || defined(__DOXYGEN__) + +#include "stm32_tim.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Number of PWM channels per PWM driver. + */ +#define PWM_CHANNELS STM32_TIM_MAX_CHANNELS + +/** + * @name STM32-specific PWM complementary output mode macros + * @{ + */ +/** + * @brief Complementary output modes mask. + * @note This is an STM32-specific setting. + */ +#define PWM_COMPLEMENTARY_OUTPUT_MASK 0xF0 + +/** + * @brief Complementary output not driven. + * @note This is an STM32-specific setting. + */ +#define PWM_COMPLEMENTARY_OUTPUT_DISABLED 0x00 + +/** + * @brief Complementary output, active is logic level one. + * @note This is an STM32-specific setting. + * @note This setting is only available if the configuration option + * @p STM32_PWM_USE_ADVANCED is set to TRUE and only for advanced + * timers TIM1 and TIM8. + */ +#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH 0x10 + +/** + * @brief Complementary output, active is logic level zero. + * @note This is an STM32-specific setting. + * @note This setting is only available if the configuration option + * @p STM32_PWM_USE_ADVANCED is set to TRUE and only for advanced + * timers TIM1 and TIM8. + */ +#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW 0x20 +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief If advanced timer features switch. + * @details If set to @p TRUE the advanced features for TIM1 and TIM8 are + * enabled. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_ADVANCED) || defined(__DOXYGEN__) +#define STM32_PWM_USE_ADVANCED FALSE +#endif + +/** + * @brief PWMD1 driver enable switch. + * @details If set to @p TRUE the support for PWMD1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM1) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM1 FALSE +#endif + +/** + * @brief PWMD2 driver enable switch. + * @details If set to @p TRUE the support for PWMD2 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM2) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM2 FALSE +#endif + +/** + * @brief PWMD3 driver enable switch. + * @details If set to @p TRUE the support for PWMD3 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM3) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM3 FALSE +#endif + +/** + * @brief PWMD4 driver enable switch. + * @details If set to @p TRUE the support for PWMD4 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM4) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM4 FALSE +#endif + +/** + * @brief PWMD5 driver enable switch. + * @details If set to @p TRUE the support for PWMD5 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM5) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM5 FALSE +#endif + +/** + * @brief PWMD8 driver enable switch. + * @details If set to @p TRUE the support for PWMD8 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM8) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM8 FALSE +#endif + +/** + * @brief PWMD9 driver enable switch. + * @details If set to @p TRUE the support for PWMD9 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_PWM_USE_TIM9) || defined(__DOXYGEN__) +#define STM32_PWM_USE_TIM9 FALSE +#endif + +/** + * @brief PWMD1 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM1_IRQ_PRIORITY 7 +#endif + +/** + * @brief PWMD2 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM2_IRQ_PRIORITY 7 +#endif + +/** + * @brief PWMD3 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM3_IRQ_PRIORITY 7 +#endif + +/** + * @brief PWMD4 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM4_IRQ_PRIORITY 7 +#endif + +/** + * @brief PWMD5 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM5_IRQ_PRIORITY 7 +#endif + +/** + * @brief PWMD8 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM8_IRQ_PRIORITY 7 +#endif +/** @} */ + +/** + * @brief PWMD9 interrupt priority level setting. + */ +#if !defined(STM32_PWM_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_PWM_TIM9_IRQ_PRIORITY 7 +#endif +/** @} */ + +/*===========================================================================*/ +/* Configuration checks. */ +/*===========================================================================*/ + +#if STM32_PWM_USE_TIM1 && !STM32_HAS_TIM1 +#error "TIM1 not present in the selected device" +#endif + +#if STM32_PWM_USE_TIM2 && !STM32_HAS_TIM2 +#error "TIM2 not present in the selected device" +#endif + +#if STM32_PWM_USE_TIM3 && !STM32_HAS_TIM3 +#error "TIM3 not present in the selected device" +#endif + +#if STM32_PWM_USE_TIM4 && !STM32_HAS_TIM4 +#error "TIM4 not present in the selected device" +#endif + +#if STM32_PWM_USE_TIM5 && !STM32_HAS_TIM5 +#error "TIM5 not present in the selected device" +#endif + +#if STM32_PWM_USE_TIM8 && !STM32_HAS_TIM8 +#error "TIM8 not present in the selected device" +#endif + +#if STM32_PWM_USE_TIM9 && !STM32_HAS_TIM9 +#error "TIM9 not present in the selected device" +#endif + +#if !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM2 && \ + !STM32_PWM_USE_TIM3 && !STM32_PWM_USE_TIM4 && \ + !STM32_PWM_USE_TIM5 && !STM32_PWM_USE_TIM8 && \ + !STM32_PWM_USE_TIM9 +#error "PWM driver activated but no TIM peripheral assigned" +#endif + +#if STM32_PWM_USE_ADVANCED && !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM8 +#error "advanced mode selected but no advanced timer assigned" +#endif + +/* Checks on allocation of TIMx units.*/ +#if STM32_PWM_USE_TIM1 +#if defined(STM32_TIM1_IS_USED) +#error "PWMD1 requires TIM1 but the timer is already used" +#else +#define STM32_TIM1_IS_USED +#endif +#endif + +#if STM32_PWM_USE_TIM2 +#if defined(STM32_TIM2_IS_USED) +#error "PWMD2 requires TIM2 but the timer is already used" +#else +#define STM32_TIM2_IS_USED +#endif +#endif + +#if STM32_PWM_USE_TIM3 +#if defined(STM32_TIM3_IS_USED) +#error "PWMD3 requires TIM3 but the timer is already used" +#else +#define STM32_TIM3_IS_USED +#endif +#endif + +#if STM32_PWM_USE_TIM4 +#if defined(STM32_TIM4_IS_USED) +#error "PWMD4 requires TIM4 but the timer is already used" +#else +#define STM32_TIM4_IS_USED +#endif +#endif + +#if STM32_PWM_USE_TIM5 +#if defined(STM32_TIM5_IS_USED) +#error "PWMD5 requires TIM5 but the timer is already used" +#else +#define STM32_TIM5_IS_USED +#endif +#endif + +#if STM32_PWM_USE_TIM8 +#if defined(STM32_TIM8_IS_USED) +#error "PWMD8 requires TIM8 but the timer is already used" +#else +#define STM32_TIM8_IS_USED +#endif +#endif + +#if STM32_PWM_USE_TIM9 +#if defined(STM32_TIM9_IS_USED) +#error "PWMD9 requires TIM9 but the timer is already used" +#else +#define STM32_TIM9_IS_USED +#endif +#endif + +/* IRQ priority checks.*/ +#if STM32_PWM_USE_TIM1 && !defined(STM32_TIM1_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM1" +#endif + +#if STM32_PWM_USE_TIM2 && !defined(STM32_TIM2_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM2" +#endif + +#if STM32_PWM_USE_TIM3 && !defined(STM32_TIM3_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM3" +#endif + +#if STM32_PWM_USE_TIM4 && !defined(STM32_TIM4_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM4" +#endif + +#if STM32_PWM_USE_TIM5 && !defined(STM32_TIM5_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM5" +#endif + +#if STM32_PWM_USE_TIM8 && !defined(STM32_TIM8_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM8_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM8" +#endif + +#if STM32_PWM_USE_TIM9 && !defined(STM32_TIM9_SUPPRESS_ISR) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM9_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to TIM9" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a PWM mode. + */ +typedef uint32_t pwmmode_t; + +/** + * @brief Type of a PWM channel. + */ +typedef uint8_t pwmchannel_t; + +/** + * @brief Type of a channels mask. + */ +typedef uint32_t pwmchnmsk_t; + +/** + * @brief Type of a PWM counter. + */ +typedef uint32_t pwmcnt_t; + +/** + * @brief Type of a PWM driver channel configuration structure. + */ +typedef struct { + /** + * @brief Channel active logic level. + */ + pwmmode_t mode; + /** + * @brief Channel callback pointer. + * @note This callback is invoked on the channel compare event. If set to + * @p NULL then the callback is disabled. + */ + pwmcallback_t callback; + /* End of the mandatory fields.*/ +} PWMChannelConfig; + +/** + * @brief Type of a PWM driver configuration structure. + */ +typedef struct { + /** + * @brief Timer clock in Hz. + * @note The low level can use assertions in order to catch invalid + * frequency specifications. + */ + uint32_t frequency; + /** + * @brief PWM period in ticks. + * @note The low level can use assertions in order to catch invalid + * period specifications. + */ + pwmcnt_t period; + /** + * @brief Periodic callback pointer. + * @note This callback is invoked on PWM counter reset. If set to + * @p NULL then the callback is disabled. + */ + pwmcallback_t callback; + /** + * @brief Channels configurations. + */ + PWMChannelConfig channels[PWM_CHANNELS]; + /* End of the mandatory fields.*/ + /** + * @brief TIM CR2 register initialization data. + * @note The value of this field should normally be equal to zero. + */ + uint32_t cr2; +#if STM32_PWM_USE_ADVANCED || defined(__DOXYGEN__) + /** + * @brief TIM BDTR (break & dead-time) register initialization data. + * @note The value of this field should normally be equal to zero. + */ \ + uint32_t bdtr; +#endif + /** + * @brief TIM DIER register initialization data. + * @note The value of this field should normally be equal to zero. + * @note Only the DMA-related bits can be specified in this field. + */ + uint32_t dier; +} PWMConfig; + +/** + * @brief Structure representing a PWM driver. + */ +struct PWMDriver { + /** + * @brief Driver state. + */ + pwmstate_t state; + /** + * @brief Current driver configuration data. + */ + const PWMConfig *config; + /** + * @brief Current PWM period in ticks. + */ + pwmcnt_t period; + /** + * @brief Mask of the enabled channels. + */ + pwmchnmsk_t enabled; + /** + * @brief Number of channels in this instance. + */ + pwmchannel_t channels; +#if defined(PWM_DRIVER_EXT_FIELDS) + PWM_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Timer base clock. + */ + uint32_t clock; + /** + * @brief Pointer to the TIMx registers block. + */ + stm32_tim_t *tim; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Changes the period the PWM peripheral. + * @details This function changes the period of a PWM unit that has already + * been activated using @p pwmStart(). + * @pre The PWM unit must have been activated using @p pwmStart(). + * @post The PWM unit period is changed to the new value. + * @note The function has effect at the next cycle start. + * @note If a period is specified that is shorter than the pulse width + * programmed in one of the channels then the behavior is not + * guaranteed. + * + * @param[in] pwmp pointer to a @p PWMDriver object + * @param[in] period new cycle time in ticks + * + * @notapi + */ +#define pwm_lld_change_period(pwmp, period) \ + ((pwmp)->tim->ARR = ((period) - 1)) + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_PWM_USE_TIM1 && !defined(__DOXYGEN__) +extern PWMDriver PWMD1; +#endif + +#if STM32_PWM_USE_TIM2 && !defined(__DOXYGEN__) +extern PWMDriver PWMD2; +#endif + +#if STM32_PWM_USE_TIM3 && !defined(__DOXYGEN__) +extern PWMDriver PWMD3; +#endif + +#if STM32_PWM_USE_TIM4 && !defined(__DOXYGEN__) +extern PWMDriver PWMD4; +#endif + +#if STM32_PWM_USE_TIM5 && !defined(__DOXYGEN__) +extern PWMDriver PWMD5; +#endif + +#if STM32_PWM_USE_TIM8 && !defined(__DOXYGEN__) +extern PWMDriver PWMD8; +#endif + +#if STM32_PWM_USE_TIM9 && !defined(__DOXYGEN__) +extern PWMDriver PWMD9; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void pwm_lld_init(void); + void pwm_lld_start(PWMDriver *pwmp); + void pwm_lld_stop(PWMDriver *pwmp); + void pwm_lld_enable_channel(PWMDriver *pwmp, + pwmchannel_t channel, + pwmcnt_t width); + void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel); + void pwm_lld_enable_periodic_notification(PWMDriver *pwmp); + void pwm_lld_disable_periodic_notification(PWMDriver *pwmp); + void pwm_lld_enable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel); + void pwm_lld_disable_channel_notification(PWMDriver *pwmp, + pwmchannel_t channel); + void pwm_lld_serve_interrupt(PWMDriver *pwmp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_PWM */ + +#endif /* _PWM_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.c b/os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.c new file mode 100644 index 000000000..cea1847ee --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.c @@ -0,0 +1,308 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/st_lld.c + * @brief ST Driver subsystem low level driver code. + * + * @addtogroup ST + * @{ + */ + +#include "hal.h" + +#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING + +#if (OSAL_ST_RESOLUTION == 32) +#define ST_ARR_INIT 0xFFFFFFFF +#else +#define ST_ARR_INIT 0x0000FFFF +#endif + +#if STM32_ST_USE_TIMER == 2 +#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM2_IS_32BITS +#error "TIM2 is not a 32bits timer" +#endif + +#if defined(STM32_TIM2_IS_USED) +#error "ST requires TIM2 but the timer is already used" +#else +#define STM32_TIM2_IS_USED +#endif + +#define ST_HANDLER STM32_TIM2_HANDLER +#define ST_NUMBER STM32_TIM2_NUMBER +#define ST_CLOCK_SRC STM32_TIMCLK1 +#define ST_ENABLE_CLOCK() rccEnableTIM2(FALSE) +#if defined(STM32F1XX) +#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM2_STOP +#elif defined(STM32L4XX) +#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM2_STOP +#else +#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM2_STOP +#endif + +#elif STM32_ST_USE_TIMER == 3 +#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM3_IS_32BITS +#error "TIM3 is not a 32bits timer" +#endif + +#if defined(STM32_TIM3_IS_USED) +#error "ST requires TIM3 but the timer is already used" +#else +#define STM32_TIM3_IS_USED +#endif + +#define ST_HANDLER STM32_TIM3_HANDLER +#define ST_NUMBER STM32_TIM3_NUMBER +#define ST_CLOCK_SRC STM32_TIMCLK1 +#define ST_ENABLE_CLOCK() rccEnableTIM3(FALSE) +#if defined(STM32F1XX) +#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM3_STOP +#elif defined(STM32L4XX) +#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM3_STOP +#else +#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM3_STOP +#endif + +#elif STM32_ST_USE_TIMER == 4 +#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM4_IS_32BITS +#error "TIM4 is not a 32bits timer" +#endif + +#if defined(STM32_TIM4_IS_USED) +#error "ST requires TIM4 but the timer is already used" +#else +#define STM32_TIM4_IS_USED +#endif + +#define ST_HANDLER STM32_TIM4_HANDLER +#define ST_NUMBER STM32_TIM4_NUMBER +#define ST_CLOCK_SRC STM32_TIMCLK1 +#define ST_ENABLE_CLOCK() rccEnableTIM4(FALSE) +#if defined(STM32F1XX) +#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM4_STOP +#elif defined(STM32L4XX) +#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM4_STOP +#else +#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM4_STOP +#endif + +#elif STM32_ST_USE_TIMER == 5 +#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM5_IS_32BITS +#error "TIM5 is not a 32bits timer" +#endif + +#if defined(STM32_TIM5_IS_USED) +#error "ST requires TIM5 but the timer is already used" +#else +#define STM32_TIM5_IS_USED +#endif + +#define ST_HANDLER STM32_TIM5_HANDLER +#define ST_NUMBER STM32_TIM5_NUMBER +#define ST_CLOCK_SRC STM32_TIMCLK1 +#define ST_ENABLE_CLOCK() rccEnableTIM5(FALSE) +#if defined(STM32F1XX) +#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM5_STOP +#elif defined(STM32L4XX) +#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM5_STOP +#else +#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM5_STOP +#endif + +#elif STM32_ST_USE_TIMER == 21 +#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM21_IS_32BITS +#error "TIM21 is not a 32bits timer" +#endif + +#if defined(STM32_TIM21_IS_USED) +#error "ST requires TIM21 but the timer is already used" +#else +#define STM32_TIM21_IS_USED +#endif + +#define ST_HANDLER STM32_TIM21_HANDLER +#define ST_NUMBER STM32_TIM21_NUMBER +#define ST_CLOCK_SRC STM32_TIMCLK2 +#define ST_ENABLE_CLOCK() rccEnableTIM21(FALSE) +#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB2_FZ_DBG_TIM21_STOP + +#elif STM32_ST_USE_TIMER == 22 +#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM22_IS_32BITS +#error "TIM21 is not a 32bits timer" +#endif + +#if defined(STM32_TIM22_IS_USED) +#error "ST requires TIM22 but the timer is already used" +#else +#define STM32_TIM22_IS_USED +#endif + +#define ST_HANDLER STM32_TIM22_HANDLER +#define ST_NUMBER STM32_TIM22_NUMBER +#define ST_CLOCK_SRC STM32_TIMCLK2 +#define ST_ENABLE_CLOCK() rccEnableTIM22(FALSE) +#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB2_FZ_DBG_TIM21_STOP + +#else +#error "STM32_ST_USE_TIMER specifies an unsupported timer" +#endif + +#if ST_CLOCK_SRC % OSAL_ST_FREQUENCY != 0 +#error "the selected ST frequency is not obtainable because integer rounding" +#endif + +#if (ST_CLOCK_SRC / OSAL_ST_FREQUENCY) - 1 > 0xFFFF +#error "the selected ST frequency is not obtainable because TIM timer prescaler limits" +#endif + +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ + +#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC + +#if STM32_HCLK % OSAL_ST_FREQUENCY != 0 +#error "the selected ST frequency is not obtainable because integer rounding" +#endif + +#if (STM32_HCLK / OSAL_ST_FREQUENCY) - 1 > 0xFFFFFF +#error "the selected ST frequency is not obtainable because SysTick timer counter limits" +#endif + +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__) +/** + * @brief System Timer vector. + * @details This interrupt is used for system tick in periodic mode. + * + * @isr + */ +OSAL_IRQ_HANDLER(SysTick_Handler) { + + OSAL_IRQ_PROLOGUE(); + + osalSysLockFromISR(); + osalOsTimerHandlerI(); + osalSysUnlockFromISR(); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ + +#if (OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING) || defined(__DOXYGEN__) +/** + * @brief TIM2 interrupt handler. + * @details This interrupt is used for system tick in free running mode. + * + * @isr + */ +OSAL_IRQ_HANDLER(ST_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + /* Note, under rare circumstances an interrupt can remain latched even if + the timer SR register has been cleared, in those cases the interrupt + is simply ignored.*/ + if ((STM32_ST_TIM->SR & TIM_SR_CC1IF) != 0U) { + STM32_ST_TIM->SR = 0U; + + osalSysLockFromISR(); + osalOsTimerHandlerI(); + osalSysUnlockFromISR(); + } + + OSAL_IRQ_EPILOGUE(); +} +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level ST driver initialization. + * + * @notapi + */ +void st_lld_init(void) { + +#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING + /* Free running counter mode.*/ + + /* Enabling timer clock.*/ + ST_ENABLE_CLOCK(); + + /* Enabling the stop mode during debug for this timer.*/ + ST_ENABLE_STOP(); + + /* Initializing the counter in free running mode.*/ + STM32_ST_TIM->PSC = (ST_CLOCK_SRC / OSAL_ST_FREQUENCY) - 1; + STM32_ST_TIM->ARR = ST_ARR_INIT; + STM32_ST_TIM->CCMR1 = 0; + STM32_ST_TIM->CCR[0] = 0; + STM32_ST_TIM->DIER = 0; + STM32_ST_TIM->CR2 = 0; + STM32_ST_TIM->EGR = TIM_EGR_UG; + STM32_ST_TIM->CR1 = TIM_CR1_CEN; + + /* IRQ enabled.*/ + nvicEnableVector(ST_NUMBER, STM32_ST_IRQ_PRIORITY); +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ + +#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC + /* Periodic systick mode, the Cortex-Mx internal systick timer is used + in this mode.*/ + SysTick->LOAD = (STM32_HCLK / OSAL_ST_FREQUENCY) - 1; + SysTick->VAL = 0; + SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | + SysTick_CTRL_ENABLE_Msk | + SysTick_CTRL_TICKINT_Msk; + + /* IRQ enabled.*/ + nvicSetSystemHandlerPriority(HANDLER_SYSTICK, STM32_ST_IRQ_PRIORITY); +#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ +} + +#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.h b/os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.h new file mode 100644 index 000000000..beefd16e2 --- /dev/null +++ b/os/hal/ports/STM32/LLD/TIMv1/hal_st_lld.h @@ -0,0 +1,210 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/st_lld.h + * @brief ST Driver subsystem low level driver header. + * @details This header is designed to be include-able without having to + * include other files from the HAL. + * + * @addtogroup ST + * @{ + */ + +#ifndef _ST_LLD_H_ +#define _ST_LLD_H_ + +#include "mcuconf.h" +#include "stm32_registry.h" +#include "stm32_tim.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief SysTick timer IRQ priority. + */ +#if !defined(STM32_ST_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_ST_IRQ_PRIORITY 8 +#endif + +/** + * @brief TIMx unit (by number) to be used for free running operations. + * @note You must select a 32 bits timer if a 32 bits @p systick_t type + * is required. + * @note Timers 2, 3, 4 and 5 are supported. + */ +#if !defined(STM32_ST_USE_TIMER) || defined(__DOXYGEN__) +#define STM32_ST_USE_TIMER 2 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_ST_USE_TIMER == 2 +#if !STM32_HAS_TIM2 +#error "TIM2 not present" +#endif +#define STM32_ST_TIM STM32_TIM2 + +#elif STM32_ST_USE_TIMER == 3 +#if !STM32_HAS_TIM3 +#error "TIM3 not present" +#endif +#define STM32_ST_TIM STM32_TIM3 + +#elif STM32_ST_USE_TIMER == 4 +#if !STM32_HAS_TIM4 +#error "TIM4 not present" +#endif +#define STM32_ST_TIM STM32_TIM4 + +#elif STM32_ST_USE_TIMER == 5 +#if !STM32_HAS_TIM5 +#error "TIM5 not present" +#endif +#define STM32_ST_TIM STM32_TIM5 + +#elif STM32_ST_USE_TIMER == 21 +#if !STM32_HAS_TIM21 +#error "TIM21 not present" +#endif +#define STM32_ST_TIM STM32_TIM21 + +#elif STM32_ST_USE_TIMER == 22 +#if !STM32_HAS_TIM22 +#error "TIM22 not present" +#endif +#define STM32_ST_TIM STM32_TIM22 + +#else +#error "STM32_ST_USE_TIMER specifies an unsupported timer" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void st_lld_init(void); +#ifdef __cplusplus +} +#endif + +/*===========================================================================*/ +/* Driver inline functions. */ +/*===========================================================================*/ + +/** + * @brief Returns the time counter value. + * + * @return The counter value. + * + * @notapi + */ +static inline systime_t st_lld_get_counter(void) { + + return (systime_t)STM32_ST_TIM->CNT; +} + +/** + * @brief Starts the alarm. + * @note Makes sure that no spurious alarms are triggered after + * this call. + * + * @param[in] time the time to be set for the first alarm + * + * @notapi + */ +static inline void st_lld_start_alarm(systime_t time) { + + STM32_ST_TIM->CCR[0] = (uint32_t)time; + STM32_ST_TIM->SR = 0; + STM32_ST_TIM->DIER = STM32_TIM_DIER_CC1IE; +} + +/** + * @brief Stops the alarm interrupt. + * + * @notapi + */ +static inline void st_lld_stop_alarm(void) { + + STM32_ST_TIM->DIER = 0; +} + +/** + * @brief Sets the alarm time. + * + * @param[in] time the time to be set for the next alarm + * + * @notapi + */ +static inline void st_lld_set_alarm(systime_t time) { + + STM32_ST_TIM->CCR[0] = (uint32_t)time; +} + +/** + * @brief Returns the current alarm time. + * + * @return The currently set alarm time. + * + * @notapi + */ +static inline systime_t st_lld_get_alarm(void) { + + return (systime_t)STM32_ST_TIM->CCR[0]; +} + +/** + * @brief Determines if the alarm is active. + * + * @return The alarm status. + * @retval false if the alarm is not active. + * @retval true is the alarm is active + * + * @notapi + */ +static inline bool st_lld_is_alarm_active(void) { + + return (bool)((STM32_ST_TIM->DIER & STM32_TIM_DIER_CC1IE) != 0); +} + +#endif /* _ST_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c b/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c deleted file mode 100644 index f7554241b..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.c +++ /dev/null @@ -1,805 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ -/* - Concepts and parts of this file have been contributed by Fabio Utzig and - Xo Wang. - */ - -/** - * @file STM32/icu_lld.c - * @brief STM32 ICU subsystem low level driver header. - * - * @addtogroup ICU - * @{ - */ - -#include "hal.h" - -#if HAL_USE_ICU || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief ICUD1 driver identifier. - * @note The driver ICUD1 allocates the complex timer TIM1 when enabled. - */ -#if STM32_ICU_USE_TIM1 || defined(__DOXYGEN__) -ICUDriver ICUD1; -#endif - -/** - * @brief ICUD2 driver identifier. - * @note The driver ICUD1 allocates the timer TIM2 when enabled. - */ -#if STM32_ICU_USE_TIM2 || defined(__DOXYGEN__) -ICUDriver ICUD2; -#endif - -/** - * @brief ICUD3 driver identifier. - * @note The driver ICUD1 allocates the timer TIM3 when enabled. - */ -#if STM32_ICU_USE_TIM3 || defined(__DOXYGEN__) -ICUDriver ICUD3; -#endif - -/** - * @brief ICUD4 driver identifier. - * @note The driver ICUD4 allocates the timer TIM4 when enabled. - */ -#if STM32_ICU_USE_TIM4 || defined(__DOXYGEN__) -ICUDriver ICUD4; -#endif - -/** - * @brief ICUD5 driver identifier. - * @note The driver ICUD5 allocates the timer TIM5 when enabled. - */ -#if STM32_ICU_USE_TIM5 || defined(__DOXYGEN__) -ICUDriver ICUD5; -#endif - -/** - * @brief ICUD8 driver identifier. - * @note The driver ICUD8 allocates the timer TIM8 when enabled. - */ -#if STM32_ICU_USE_TIM8 || defined(__DOXYGEN__) -ICUDriver ICUD8; -#endif - -/** - * @brief ICUD9 driver identifier. - * @note The driver ICUD9 allocates the timer TIM9 when enabled. - */ -#if STM32_ICU_USE_TIM9 || defined(__DOXYGEN__) -ICUDriver ICUD9; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static bool icu_lld_wait_edge(ICUDriver *icup) { - uint32_t sr; - bool result; - - /* Polled mode so re-enabling the interrupts while the operation is - performed.*/ - osalSysUnlock(); - - /* Polling the right bit depending on the input channel.*/ - if (icup->config->channel == ICU_CHANNEL_1) { - /* Waiting for an edge.*/ - while (((sr = icup->tim->SR) & - (STM32_TIM_SR_CC1IF | STM32_TIM_SR_UIF)) == 0) - ; - } - else { - /* Waiting for an edge.*/ - while (((sr = icup->tim->SR) & - (STM32_TIM_SR_CC2IF | STM32_TIM_SR_UIF)) == 0) - ; - } - - /* Edge or overflow?*/ - result = (sr & STM32_TIM_SR_UIF) != 0 ? true : false; - - /* Done, disabling interrupts again.*/ - osalSysLock(); - - /* Resetting all flags.*/ - icup->tim->SR &= ~(STM32_TIM_SR_CC1IF | - STM32_TIM_SR_CC2IF | - STM32_TIM_SR_UIF); - - return result; -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_ICU_USE_TIM1 || defined(__DOXYGEN__) -#if !defined(STM32_TIM1_SUPPRESS_ISR) -#if !defined(STM32_TIM1_UP_HANDLER) -#error "STM32_TIM1_UP_HANDLER not defined" -#endif -/** - * @brief TIM1 compare interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD1); - - OSAL_IRQ_EPILOGUE(); -} - -#if !defined(STM32_TIM1_CC_HANDLER) -#error "STM32_TIM1_CC_HANDLER not defined" -#endif -/** - * @brief TIM1 compare interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM1_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM1 */ - -#if STM32_ICU_USE_TIM2 || defined(__DOXYGEN__) -#if !defined(STM32_TIM2_SUPPRESS_ISR) -#if !defined(STM32_TIM2_HANDLER) -#error "STM32_TIM2_HANDLER not defined" -#endif -/** - * @brief TIM2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM2_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM2 */ - -#if STM32_ICU_USE_TIM3 || defined(__DOXYGEN__) -#if !defined(STM32_TIM3_SUPPRESS_ISR) -#if !defined(STM32_TIM3_HANDLER) -#error "STM32_TIM3_HANDLER not defined" -#endif -/** - * @brief TIM3 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM3_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM3 */ - -#if STM32_ICU_USE_TIM4 || defined(__DOXYGEN__) -#if !defined(STM32_TIM4_SUPPRESS_ISR) -#if !defined(STM32_TIM4_HANDLER) -#error "STM32_TIM4_HANDLER not defined" -#endif -/** - * @brief TIM4 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM4_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM4 */ - -#if STM32_ICU_USE_TIM5 || defined(__DOXYGEN__) -#if !defined(STM32_TIM5_SUPPRESS_ISR) -#if !defined(STM32_TIM5_HANDLER) -#error "STM32_TIM5_HANDLER not defined" -#endif -/** - * @brief TIM5 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM5_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM5 */ - -#if STM32_ICU_USE_TIM8 || defined(__DOXYGEN__) -#if !defined(STM32_TIM8_SUPPRESS_ISR) -#if !defined(STM32_TIM8_UP_HANDLER) -#error "STM32_TIM8_UP_HANDLER not defined" -#endif -/** - * @brief TIM8 compare interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD8); - - OSAL_IRQ_EPILOGUE(); -} - -#if !defined(STM32_TIM8_CC_HANDLER) -#error "STM32_TIM8_CC_HANDLER not defined" -#endif -/** - * @brief TIM8 compare interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD8); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM8_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM8 */ - -#if STM32_ICU_USE_TIM9 || defined(__DOXYGEN__) -#if !defined(STM32_TIM9_SUPPRESS_ISR) -#if !defined(STM32_TIM9_HANDLER) -#error "STM32_TIM9_HANDLER not defined" -#endif -/** - * @brief TIM9 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM9_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - icu_lld_serve_interrupt(&ICUD9); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM9_SUPPRESS_ISR) */ -#endif /* STM32_ICU_USE_TIM9 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ICU driver initialization. - * - * @notapi - */ -void icu_lld_init(void) { - -#if STM32_ICU_USE_TIM1 - /* Driver initialization.*/ - icuObjectInit(&ICUD1); - ICUD1.tim = STM32_TIM1; -#endif - -#if STM32_ICU_USE_TIM2 - /* Driver initialization.*/ - icuObjectInit(&ICUD2); - ICUD2.tim = STM32_TIM2; -#endif - -#if STM32_ICU_USE_TIM3 - /* Driver initialization.*/ - icuObjectInit(&ICUD3); - ICUD3.tim = STM32_TIM3; -#endif - -#if STM32_ICU_USE_TIM4 - /* Driver initialization.*/ - icuObjectInit(&ICUD4); - ICUD4.tim = STM32_TIM4; -#endif - -#if STM32_ICU_USE_TIM5 - /* Driver initialization.*/ - icuObjectInit(&ICUD5); - ICUD5.tim = STM32_TIM5; -#endif - -#if STM32_ICU_USE_TIM8 - /* Driver initialization.*/ - icuObjectInit(&ICUD8); - ICUD8.tim = STM32_TIM8; -#endif - -#if STM32_ICU_USE_TIM9 - /* Driver initialization.*/ - icuObjectInit(&ICUD9); - ICUD9.tim = STM32_TIM9; -#endif -} - -/** - * @brief Configures and activates the ICU peripheral. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_start(ICUDriver *icup) { - uint32_t psc; - - osalDbgAssert((icup->config->channel == ICU_CHANNEL_1) || - (icup->config->channel == ICU_CHANNEL_2), - "invalid input"); - - if (icup->state == ICU_STOP) { - /* Clock activation and timer reset.*/ -#if STM32_ICU_USE_TIM1 - if (&ICUD1 == icup) { - rccEnableTIM1(FALSE); - rccResetTIM1(); -#if !defined(STM32_TIM1_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM1_UP_NUMBER, STM32_ICU_TIM1_IRQ_PRIORITY); - nvicEnableVector(STM32_TIM1_CC_NUMBER, STM32_ICU_TIM1_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM1CLK) - icup->clock = STM32_TIM1CLK; -#else - icup->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_ICU_USE_TIM2 - if (&ICUD2 == icup) { - rccEnableTIM2(FALSE); - rccResetTIM2(); -#if !defined(STM32_TIM2_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM2_NUMBER, STM32_ICU_TIM2_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM2CLK) - icup->clock = STM32_TIM2CLK; -#else - icup->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_ICU_USE_TIM3 - if (&ICUD3 == icup) { - rccEnableTIM3(FALSE); - rccResetTIM3(); -#if !defined(STM32_TIM3_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM3_NUMBER, STM32_ICU_TIM3_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM3CLK) - icup->clock = STM32_TIM3CLK; -#else - icup->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_ICU_USE_TIM4 - if (&ICUD4 == icup) { - rccEnableTIM4(FALSE); - rccResetTIM4(); -#if !defined(STM32_TIM4_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM4_NUMBER, STM32_ICU_TIM4_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM4CLK) - icup->clock = STM32_TIM4CLK; -#else - icup->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_ICU_USE_TIM5 - if (&ICUD5 == icup) { - rccEnableTIM5(FALSE); - rccResetTIM5(); -#if !defined(STM32_TIM5_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM5_NUMBER, STM32_ICU_TIM5_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM5CLK) - icup->clock = STM32_TIM5CLK; -#else - icup->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_ICU_USE_TIM8 - if (&ICUD8 == icup) { - rccEnableTIM8(FALSE); - rccResetTIM8(); -#if !defined(STM32_TIM8_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM8_UP_NUMBER, STM32_ICU_TIM8_IRQ_PRIORITY); - nvicEnableVector(STM32_TIM8_CC_NUMBER, STM32_ICU_TIM8_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM8CLK) - icup->clock = STM32_TIM8CLK; -#else - icup->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_ICU_USE_TIM9 - if (&ICUD9 == icup) { - rccEnableTIM9(FALSE); - rccResetTIM9(); -#if !defined(STM32_TIM9_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM9_NUMBER, STM32_ICU_TIM9_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM9CLK) - icup->clock = STM32_TIM9CLK; -#else - icup->clock = STM32_TIMCLK2; -#endif - } -#endif - } - else { - /* Driver re-configuration scenario, it must be stopped first.*/ - icup->tim->CR1 = 0; /* Timer disabled. */ - icup->tim->CCR[0] = 0; /* Comparator 1 disabled. */ - icup->tim->CCR[1] = 0; /* Comparator 2 disabled. */ - icup->tim->CNT = 0; /* Counter reset to zero. */ - } - - /* Timer configuration.*/ - icup->tim->SR = 0; /* Clear eventual pending IRQs. */ - icup->tim->DIER = icup->config->dier & /* DMA-related DIER settings. */ - ~STM32_TIM_DIER_IRQ_MASK; - psc = (icup->clock / icup->config->frequency) - 1; - osalDbgAssert((psc <= 0xFFFF) && - ((psc + 1) * icup->config->frequency) == icup->clock, - "invalid frequency"); - icup->tim->PSC = psc; - icup->tim->ARR = 0xFFFF; - - if (icup->config->channel == ICU_CHANNEL_1) { - /* Selected input 1. - CCMR1_CC1S = 01 = CH1 Input on TI1. - CCMR1_CC2S = 10 = CH2 Input on TI1.*/ - icup->tim->CCMR1 = STM32_TIM_CCMR1_CC1S(1) | STM32_TIM_CCMR1_CC2S(2); - - /* SMCR_TS = 101, input is TI1FP1. - SMCR_SMS = 100, reset on rising edge.*/ - icup->tim->SMCR = STM32_TIM_SMCR_TS(5) | STM32_TIM_SMCR_SMS(4); - - /* The CCER settings depend on the selected trigger mode. - ICU_INPUT_ACTIVE_HIGH: Active on rising edge, idle on falling edge. - ICU_INPUT_ACTIVE_LOW: Active on falling edge, idle on rising edge.*/ - if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH) - icup->tim->CCER = STM32_TIM_CCER_CC1E | - STM32_TIM_CCER_CC2E | STM32_TIM_CCER_CC2P; - else - icup->tim->CCER = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P | - STM32_TIM_CCER_CC2E; - - /* Direct pointers to the capture registers in order to make reading - data faster from within callbacks.*/ - icup->wccrp = &icup->tim->CCR[1]; - icup->pccrp = &icup->tim->CCR[0]; - } - else { - /* Selected input 2. - CCMR1_CC1S = 10 = CH1 Input on TI2. - CCMR1_CC2S = 01 = CH2 Input on TI2.*/ - icup->tim->CCMR1 = STM32_TIM_CCMR1_CC1S(2) | STM32_TIM_CCMR1_CC2S(1); - - /* SMCR_TS = 110, input is TI2FP2. - SMCR_SMS = 100, reset on rising edge.*/ - icup->tim->SMCR = STM32_TIM_SMCR_TS(6) | STM32_TIM_SMCR_SMS(4); - - /* The CCER settings depend on the selected trigger mode. - ICU_INPUT_ACTIVE_HIGH: Active on rising edge, idle on falling edge. - ICU_INPUT_ACTIVE_LOW: Active on falling edge, idle on rising edge.*/ - if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH) - icup->tim->CCER = STM32_TIM_CCER_CC1E | STM32_TIM_CCER_CC1P | - STM32_TIM_CCER_CC2E; - else - icup->tim->CCER = STM32_TIM_CCER_CC1E | - STM32_TIM_CCER_CC2E | STM32_TIM_CCER_CC2P; - - /* Direct pointers to the capture registers in order to make reading - data faster from within callbacks.*/ - icup->wccrp = &icup->tim->CCR[0]; - icup->pccrp = &icup->tim->CCR[1]; - } -} - -/** - * @brief Deactivates the ICU peripheral. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_stop(ICUDriver *icup) { - - if (icup->state == ICU_READY) { - /* Clock deactivation.*/ - icup->tim->CR1 = 0; /* Timer disabled. */ - icup->tim->DIER = 0; /* All IRQs disabled. */ - icup->tim->SR = 0; /* Clear eventual pending IRQs. */ - -#if STM32_ICU_USE_TIM1 - if (&ICUD1 == icup) { -#if !defined(STM32_TIM1_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM1_UP_NUMBER); - nvicDisableVector(STM32_TIM1_CC_NUMBER); -#endif - rccDisableTIM1(FALSE); - } -#endif - -#if STM32_ICU_USE_TIM2 - if (&ICUD2 == icup) { -#if !defined(STM32_TIM2_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM2_NUMBER); -#endif - rccDisableTIM2(FALSE); - } -#endif - -#if STM32_ICU_USE_TIM3 - if (&ICUD3 == icup) { -#if !defined(STM32_TIM3_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM3_NUMBER); -#endif - rccDisableTIM3(FALSE); - } -#endif - -#if STM32_ICU_USE_TIM4 - if (&ICUD4 == icup) { -#if !defined(STM32_TIM4_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM4_NUMBER); -#endif - rccDisableTIM4(FALSE); - } -#endif - -#if STM32_ICU_USE_TIM5 - if (&ICUD5 == icup) { -#if !defined(STM32_TIM5_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM5_NUMBER); -#endif - rccDisableTIM5(FALSE); - } -#endif - -#if STM32_ICU_USE_TIM8 - if (&ICUD8 == icup) { -#if !defined(STM32_TIM8_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM8_UP_NUMBER); - nvicDisableVector(STM32_TIM8_CC_NUMBER); -#endif - rccDisableTIM8(FALSE); - } -#endif - -#if STM32_ICU_USE_TIM9 - if (&ICUD9 == icup) { -#if !defined(STM32_TIM9_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM9_NUMBER); -#endif - rccDisableTIM9(FALSE); - } -#endif - } -} - -/** - * @brief Starts the input capture. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_start_capture(ICUDriver *icup) { - - /* Triggering an UG and clearing the IRQ status.*/ - icup->tim->EGR |= STM32_TIM_EGR_UG; - icup->tim->SR = 0; - - /* Timer is started.*/ - icup->tim->CR1 = STM32_TIM_CR1_URS | STM32_TIM_CR1_CEN; -} - -/** - * @brief Waits for a completed capture. - * @note The operation is performed in polled mode. - * @note In order to use this function notifications must be disabled. - * - * @param[in] icup pointer to the @p ICUDriver object - * @return The capture status. - * @retval false if the capture is successful. - * @retval true if a timer overflow occurred. - * - * @notapi - */ -bool icu_lld_wait_capture(ICUDriver *icup) { - - /* If the driver is still in the ICU_WAITING state then we need to wait - for the first activation edge.*/ - if (icup->state == ICU_WAITING) - if (icu_lld_wait_edge(icup)) - return true; - - /* This edge marks the availability of a capture result.*/ - return icu_lld_wait_edge(icup); -} - -/** - * @brief Stops the input capture. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_stop_capture(ICUDriver *icup) { - - /* Timer stopped.*/ - icup->tim->CR1 = 0; - - /* All interrupts disabled.*/ - icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK; -} - -/** - * @brief Enables notifications. - * @pre The ICU unit must have been activated using @p icuStart(). - * @note If the notification is already enabled then the call has no effect. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_enable_notifications(ICUDriver *icup) { - uint32_t dier = icup->tim->DIER; - - /* If interrupts were already enabled then the operation is skipped. - This is done in order to avoid clearing the SR and risk losing - pending interrupts.*/ - if ((dier & STM32_TIM_DIER_IRQ_MASK) == 0) { - /* Previously triggered IRQs are ignored, status cleared.*/ - icup->tim->SR = 0; - - if (icup->config->channel == ICU_CHANNEL_1) { - /* Enabling periodic callback on CC1.*/ - dier |= STM32_TIM_DIER_CC1IE; - - /* Optionally enabling width callback on CC2.*/ - if (icup->config->width_cb != NULL) - dier |= STM32_TIM_DIER_CC2IE; - } - else { - /* Enabling periodic callback on CC2.*/ - dier |= STM32_TIM_DIER_CC2IE; - - /* Optionally enabling width callback on CC1.*/ - if (icup->config->width_cb != NULL) - dier |= STM32_TIM_DIER_CC1IE; - } - - /* If an overflow callback is defined then also the overflow callback - is enabled.*/ - if (icup->config->overflow_cb != NULL) - dier |= STM32_TIM_DIER_UIE; - - /* One single atomic write.*/ - icup->tim->DIER = dier; - } -} - -/** - * @brief Disables notifications. - * @pre The ICU unit must have been activated using @p icuStart(). - * @note If the notification is already disabled then the call has no effect. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_disable_notifications(ICUDriver *icup) { - - /* All interrupts disabled.*/ - icup->tim->DIER &= ~STM32_TIM_DIER_IRQ_MASK; -} - -/** - * @brief Shared IRQ handler. - * - * @param[in] icup pointer to the @p ICUDriver object - * - * @notapi - */ -void icu_lld_serve_interrupt(ICUDriver *icup) { - uint32_t sr; - - sr = icup->tim->SR; - sr &= icup->tim->DIER & STM32_TIM_DIER_IRQ_MASK; - icup->tim->SR = ~sr; - if (icup->config->channel == ICU_CHANNEL_1) { - if ((sr & STM32_TIM_SR_CC2IF) != 0) - _icu_isr_invoke_width_cb(icup); - if ((sr & STM32_TIM_SR_CC1IF) != 0) - _icu_isr_invoke_period_cb(icup); - } - else { - if ((sr & STM32_TIM_SR_CC1IF) != 0) - _icu_isr_invoke_width_cb(icup); - if ((sr & STM32_TIM_SR_CC2IF) != 0) - _icu_isr_invoke_period_cb(icup); - } - if ((sr & STM32_TIM_SR_UIF) != 0) - _icu_isr_invoke_overflow_cb(icup); -} - -#endif /* HAL_USE_ICU */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h b/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h deleted file mode 100644 index 25a4f627a..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/icu_lld.h +++ /dev/null @@ -1,487 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/icu_lld.h - * @brief STM32 ICU subsystem low level driver header. - * - * @addtogroup ICU - * @{ - */ - -#ifndef _ICU_LLD_H_ -#define _ICU_LLD_H_ - -#if HAL_USE_ICU || defined(__DOXYGEN__) - -#include "stm32_tim.h" - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief ICUD1 driver enable switch. - * @details If set to @p TRUE the support for ICUD1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM1) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM1 FALSE -#endif - -/** - * @brief ICUD2 driver enable switch. - * @details If set to @p TRUE the support for ICUD2 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM2) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM2 FALSE -#endif - -/** - * @brief ICUD3 driver enable switch. - * @details If set to @p TRUE the support for ICUD3 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM3) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM3 FALSE -#endif - -/** - * @brief ICUD4 driver enable switch. - * @details If set to @p TRUE the support for ICUD4 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM4) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM4 FALSE -#endif - -/** - * @brief ICUD5 driver enable switch. - * @details If set to @p TRUE the support for ICUD5 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM5) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM5 FALSE -#endif - -/** - * @brief ICUD8 driver enable switch. - * @details If set to @p TRUE the support for ICUD8 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM8) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM8 FALSE -#endif - -/** - * @brief ICUD9 driver enable switch. - * @details If set to @p TRUE the support for ICUD9 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_ICU_USE_TIM9) || defined(__DOXYGEN__) -#define STM32_ICU_USE_TIM9 FALSE -#endif - -/** - * @brief ICUD1 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM1_IRQ_PRIORITY 7 -#endif - -/** - * @brief ICUD2 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM2_IRQ_PRIORITY 7 -#endif - -/** - * @brief ICUD3 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM3_IRQ_PRIORITY 7 -#endif - -/** - * @brief ICUD4 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM4_IRQ_PRIORITY 7 -#endif - -/** - * @brief ICUD5 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM5_IRQ_PRIORITY 7 -#endif - -/** - * @brief ICUD8 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM8_IRQ_PRIORITY 7 -#endif - -/** - * @brief ICUD9 interrupt priority level setting. - */ -#if !defined(STM32_ICU_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ICU_TIM9_IRQ_PRIORITY 7 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_ICU_USE_TIM1 && !STM32_HAS_TIM1 -#error "TIM1 not present in the selected device" -#endif - -#if STM32_ICU_USE_TIM2 && !STM32_HAS_TIM2 -#error "TIM2 not present in the selected device" -#endif - -#if STM32_ICU_USE_TIM3 && !STM32_HAS_TIM3 -#error "TIM3 not present in the selected device" -#endif - -#if STM32_ICU_USE_TIM4 && !STM32_HAS_TIM4 -#error "TIM4 not present in the selected device" -#endif - -#if STM32_ICU_USE_TIM5 && !STM32_HAS_TIM5 -#error "TIM5 not present in the selected device" -#endif - -#if STM32_ICU_USE_TIM8 && !STM32_HAS_TIM8 -#error "TIM8 not present in the selected device" -#endif - -#if STM32_ICU_USE_TIM9 && !STM32_HAS_TIM9 -#error "TIM9 not present in the selected device" -#endif - -#if !STM32_ICU_USE_TIM1 && !STM32_ICU_USE_TIM2 && \ - !STM32_ICU_USE_TIM3 && !STM32_ICU_USE_TIM4 && \ - !STM32_ICU_USE_TIM5 && !STM32_ICU_USE_TIM8 && \ - !STM32_ICU_USE_TIM9 -#error "ICU driver activated but no TIM peripheral assigned" -#endif - -/* Checks on allocation of TIMx units.*/ -#if STM32_ICU_USE_TIM1 -#if defined(STM32_TIM1_IS_USED) -#error "ICUD1 requires TIM1 but the timer is already used" -#else -#define STM32_TIM1_IS_USED -#endif -#endif - -#if STM32_ICU_USE_TIM2 -#if defined(STM32_TIM2_IS_USED) -#error "ICUD2 requires TIM2 but the timer is already used" -#else -#define STM32_TIM2_IS_USED -#endif -#endif - -#if STM32_ICU_USE_TIM3 -#if defined(STM32_TIM3_IS_USED) -#error "ICUD3 requires TIM3 but the timer is already used" -#else -#define STM32_TIM3_IS_USED -#endif -#endif - -#if STM32_ICU_USE_TIM4 -#if defined(STM32_TIM4_IS_USED) -#error "ICUD4 requires TIM4 but the timer is already used" -#else -#define STM32_TIM4_IS_USED -#endif -#endif - -#if STM32_ICU_USE_TIM5 -#if defined(STM32_TIM5_IS_USED) -#error "ICUD5 requires TIM5 but the timer is already used" -#else -#define STM32_TIM5_IS_USED -#endif -#endif - -#if STM32_ICU_USE_TIM8 -#if defined(STM32_TIM8_IS_USED) -#error "ICUD8 requires TIM8 but the timer is already used" -#else -#define STM32_TIM8_IS_USED -#endif -#endif - -#if STM32_ICU_USE_TIM9 -#if defined(STM32_TIM9_IS_USED) -#error "ICUD9 requires TIM9 but the timer is already used" -#else -#define STM32_TIM9_IS_USED -#endif -#endif - -/* IRQ priority checks.*/ -#if STM32_ICU_USE_TIM1 && !defined(STM32_TIM1_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM1" -#endif - -#if STM32_ICU_USE_TIM2 && !defined(STM32_TIM2_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM2" -#endif - -#if STM32_ICU_USE_TIM3 && !defined(STM32_TIM3_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM3" -#endif - -#if STM32_ICU_USE_TIM4 && !defined(STM32_TIM4_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM4" -#endif - -#if STM32_ICU_USE_TIM5 && !defined(STM32_TIM5_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM5" -#endif - -#if STM32_ICU_USE_TIM8 && !defined(STM32_TIM8_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM8_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM8" -#endif - -#if STM32_ICU_USE_TIM9 && !defined(STM32_TIM9_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_ICU_TIM9_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM9" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief ICU driver mode. - */ -typedef enum { - ICU_INPUT_ACTIVE_HIGH = 0, /**< Trigger on rising edge. */ - ICU_INPUT_ACTIVE_LOW = 1, /**< Trigger on falling edge. */ -} icumode_t; - -/** - * @brief ICU frequency type. - */ -typedef uint32_t icufreq_t; - -/** - * @brief ICU channel type. - */ -typedef enum { - ICU_CHANNEL_1 = 0, /**< Use TIMxCH1. */ - ICU_CHANNEL_2 = 1, /**< Use TIMxCH2. */ -} icuchannel_t; - -/** - * @brief ICU counter type. - */ -typedef uint32_t icucnt_t; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Driver mode. - */ - icumode_t mode; - /** - * @brief Timer clock in Hz. - * @note The low level can use assertions in order to catch invalid - * frequency specifications. - */ - icufreq_t frequency; - /** - * @brief Callback for pulse width measurement. - */ - icucallback_t width_cb; - /** - * @brief Callback for cycle period measurement. - */ - icucallback_t period_cb; - /** - * @brief Callback for timer overflow. - */ - icucallback_t overflow_cb; - /* End of the mandatory fields.*/ - /** - * @brief Timer input channel to be used. - * @note Only inputs TIMx 1 and 2 are supported. - */ - icuchannel_t channel; - /** - * @brief TIM DIER register initialization data. - * @note The value of this field should normally be equal to zero. - * @note Only the DMA-related bits can be specified in this field. - */ - uint32_t dier; -} ICUConfig; - -/** - * @brief Structure representing an ICU driver. - */ -struct ICUDriver { - /** - * @brief Driver state. - */ - icustate_t state; - /** - * @brief Current configuration data. - */ - const ICUConfig *config; -#if defined(ICU_DRIVER_EXT_FIELDS) - ICU_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Timer base clock. - */ - uint32_t clock; - /** - * @brief Pointer to the TIMx registers block. - */ - stm32_tim_t *tim; - /** - * @brief CCR register used for width capture. - */ - volatile uint32_t *wccrp; - /** - * @brief CCR register used for period capture. - */ - volatile uint32_t *pccrp; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Returns the width of the latest pulse. - * @details The pulse width is defined as number of ticks between the start - * edge and the stop edge. - * - * @param[in] icup pointer to the @p ICUDriver object - * @return The number of ticks. - * - * @notapi - */ -#define icu_lld_get_width(icup) (*((icup)->wccrp) + 1) - -/** - * @brief Returns the width of the latest cycle. - * @details The cycle width is defined as number of ticks between a start - * edge and the next start edge. - * - * @param[in] icup pointer to the @p ICUDriver object - * @return The number of ticks. - * - * @notapi - */ -#define icu_lld_get_period(icup) (*((icup)->pccrp) + 1) - -/** - * @brief Check on notifications status. - * - * @param[in] icup pointer to the @p ICUDriver object - * @return The notifications status. - * @retval false if notifications are not enabled. - * @retval true if notifications are enabled. - * - * @notapi - */ -#define icu_lld_are_notifications_enabled(icup) \ - (bool)(((icup)->tim->DIER & STM32_TIM_DIER_IRQ_MASK) != 0) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_ICU_USE_TIM1 && !defined(__DOXYGEN__) -extern ICUDriver ICUD1; -#endif - -#if STM32_ICU_USE_TIM2 && !defined(__DOXYGEN__) -extern ICUDriver ICUD2; -#endif - -#if STM32_ICU_USE_TIM3 && !defined(__DOXYGEN__) -extern ICUDriver ICUD3; -#endif - -#if STM32_ICU_USE_TIM4 && !defined(__DOXYGEN__) -extern ICUDriver ICUD4; -#endif - -#if STM32_ICU_USE_TIM5 && !defined(__DOXYGEN__) -extern ICUDriver ICUD5; -#endif - -#if STM32_ICU_USE_TIM8 && !defined(__DOXYGEN__) -extern ICUDriver ICUD8; -#endif - -#if STM32_ICU_USE_TIM9 && !defined(__DOXYGEN__) -extern ICUDriver ICUD9; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void icu_lld_init(void); - void icu_lld_start(ICUDriver *icup); - void icu_lld_stop(ICUDriver *icup); - void icu_lld_start_capture(ICUDriver *icup); - bool icu_lld_wait_capture(ICUDriver *icup); - void icu_lld_stop_capture(ICUDriver *icup); - void icu_lld_enable_notifications(ICUDriver *icup); - void icu_lld_disable_notifications(ICUDriver *icup); - void icu_lld_serve_interrupt(ICUDriver *icup); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_ICU */ - -#endif /* _ICU_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c b/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c deleted file mode 100644 index 7837b4d02..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/pwm_lld.c - * @brief STM32 PWM subsystem low level driver header. - * - * @addtogroup PWM - * @{ - */ - -#include "hal.h" - -#if HAL_USE_PWM || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** - * @brief PWMD1 driver identifier. - * @note The driver PWMD1 allocates the complex timer TIM1 when enabled. - */ -#if STM32_PWM_USE_TIM1 || defined(__DOXYGEN__) -PWMDriver PWMD1; -#endif - -/** - * @brief PWMD2 driver identifier. - * @note The driver PWMD2 allocates the timer TIM2 when enabled. - */ -#if STM32_PWM_USE_TIM2 || defined(__DOXYGEN__) -PWMDriver PWMD2; -#endif - -/** - * @brief PWMD3 driver identifier. - * @note The driver PWMD3 allocates the timer TIM3 when enabled. - */ -#if STM32_PWM_USE_TIM3 || defined(__DOXYGEN__) -PWMDriver PWMD3; -#endif - -/** - * @brief PWMD4 driver identifier. - * @note The driver PWMD4 allocates the timer TIM4 when enabled. - */ -#if STM32_PWM_USE_TIM4 || defined(__DOXYGEN__) -PWMDriver PWMD4; -#endif - -/** - * @brief PWMD5 driver identifier. - * @note The driver PWMD5 allocates the timer TIM5 when enabled. - */ -#if STM32_PWM_USE_TIM5 || defined(__DOXYGEN__) -PWMDriver PWMD5; -#endif - -/** - * @brief PWMD8 driver identifier. - * @note The driver PWMD8 allocates the timer TIM8 when enabled. - */ -#if STM32_PWM_USE_TIM8 || defined(__DOXYGEN__) -PWMDriver PWMD8; -#endif - -/** - * @brief PWMD9 driver identifier. - * @note The driver PWMD9 allocates the timer TIM9 when enabled. - */ -#if STM32_PWM_USE_TIM9 || defined(__DOXYGEN__) -PWMDriver PWMD9; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_PWM_USE_TIM1 || defined(__DOXYGEN__) -#if !defined(STM32_TIM1_SUPPRESS_ISR) -#if !defined(STM32_TIM1_UP_HANDLER) -#error "STM32_TIM1_UP_HANDLER not defined" -#endif -/** - * @brief TIM1 update interrupt handler. - * @note It is assumed that this interrupt is only activated if the callback - * pointer is not equal to @p NULL in order to not perform an extra - * check in a potentially critical interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM1_UP_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD1); - - OSAL_IRQ_EPILOGUE(); -} - -#if !defined(STM32_TIM1_CC_HANDLER) -#error "STM32_TIM1_CC_HANDLER not defined" -#endif -/** - * @brief TIM1 compare interrupt handler. - * @note It is assumed that the various sources are only activated if the - * associated callback pointer is not equal to @p NULL in order to not - * perform an extra check in a potentially critical interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM1_CC_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM1_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM1 */ - -#if STM32_PWM_USE_TIM2 || defined(__DOXYGEN__) -#if !defined(STM32_TIM2_SUPPRESS_ISR) -#if !defined(STM32_TIM2_HANDLER) -#error "STM32_TIM2_HANDLER not defined" -#endif -/** - * @brief TIM2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM2_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM2 */ - -#if STM32_PWM_USE_TIM3 || defined(__DOXYGEN__) -#if !defined(STM32_TIM3_SUPPRESS_ISR) -#if !defined(STM32_TIM3_HANDLER) -#error "STM32_TIM3_HANDLER not defined" -#endif -/** - * @brief TIM3 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM3_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM3 */ - -#if STM32_PWM_USE_TIM4 || defined(__DOXYGEN__) -#if !defined(STM32_TIM4_SUPPRESS_ISR) -#if !defined(STM32_TIM4_HANDLER) -#error "STM32_TIM4_HANDLER not defined" -#endif -/** - * @brief TIM4 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM4_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM4 */ - -#if STM32_PWM_USE_TIM5 || defined(__DOXYGEN__) -#if !defined(STM32_TIM5_SUPPRESS_ISR) -#if !defined(STM32_TIM5_HANDLER) -#error "STM32_TIM5_HANDLER not defined" -#endif -/** - * @brief TIM5 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM5_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM5 */ - -#if STM32_PWM_USE_TIM8 || defined(__DOXYGEN__) -#if !defined(STM32_TIM8_SUPPRESS_ISR) -#if !defined(STM32_TIM8_UP_HANDLER) -#error "STM32_TIM8_UP_HANDLER not defined" -#endif -/** - * @brief TIM8 update interrupt handler. - * @note It is assumed that this interrupt is only activated if the callback - * pointer is not equal to @p NULL in order to not perform an extra - * check in a potentially critical interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM8_UP_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD8); - - OSAL_IRQ_EPILOGUE(); -} - -#if !defined(STM32_TIM8_CC_HANDLER) -#error "STM32_TIM8_CC_HANDLER not defined" -#endif -/** - * @brief TIM8 compare interrupt handler. - * @note It is assumed that the various sources are only activated if the - * associated callback pointer is not equal to @p NULL in order to not - * perform an extra check in a potentially critical interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM8_CC_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD8); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM8_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM8 */ - -#if STM32_PWM_USE_TIM9 || defined(__DOXYGEN__) -#if !defined(STM32_TIM9_SUPPRESS_ISR) -#if !defined(STM32_TIM9_HANDLER) -#error "STM32_TIM9_HANDLER not defined" -#endif -/** - * @brief TIM9 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_TIM9_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - pwm_lld_serve_interrupt(&PWMD9); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* !defined(STM32_TIM9_SUPPRESS_ISR) */ -#endif /* STM32_PWM_USE_TIM9 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level PWM driver initialization. - * - * @notapi - */ -void pwm_lld_init(void) { - -#if STM32_PWM_USE_TIM1 - /* Driver initialization.*/ - pwmObjectInit(&PWMD1); - PWMD1.channels = STM32_TIM1_CHANNELS; - PWMD1.tim = STM32_TIM1; -#endif - -#if STM32_PWM_USE_TIM2 - /* Driver initialization.*/ - pwmObjectInit(&PWMD2); - PWMD2.channels = STM32_TIM2_CHANNELS; - PWMD2.tim = STM32_TIM2; -#endif - -#if STM32_PWM_USE_TIM3 - /* Driver initialization.*/ - pwmObjectInit(&PWMD3); - PWMD3.channels = STM32_TIM3_CHANNELS; - PWMD3.tim = STM32_TIM3; -#endif - -#if STM32_PWM_USE_TIM4 - /* Driver initialization.*/ - pwmObjectInit(&PWMD4); - PWMD4.channels = STM32_TIM4_CHANNELS; - PWMD4.tim = STM32_TIM4; -#endif - -#if STM32_PWM_USE_TIM5 - /* Driver initialization.*/ - pwmObjectInit(&PWMD5); - PWMD5.channels = STM32_TIM5_CHANNELS; - PWMD5.tim = STM32_TIM5; -#endif - -#if STM32_PWM_USE_TIM8 - /* Driver initialization.*/ - pwmObjectInit(&PWMD8); - PWMD8.channels = STM32_TIM8_CHANNELS; - PWMD8.tim = STM32_TIM8; -#endif - -#if STM32_PWM_USE_TIM9 - /* Driver initialization.*/ - pwmObjectInit(&PWMD9); - PWMD9.channels = STM32_TIM9_CHANNELS; - PWMD9.tim = STM32_TIM9; -#endif -} - -/** - * @brief Configures and activates the PWM peripheral. - * @note Starting a driver that is already in the @p PWM_READY state - * disables all the active channels. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * - * @notapi - */ -void pwm_lld_start(PWMDriver *pwmp) { - uint32_t psc; - uint32_t ccer; - - if (pwmp->state == PWM_STOP) { - /* Clock activation and timer reset.*/ -#if STM32_PWM_USE_TIM1 - if (&PWMD1 == pwmp) { - rccEnableTIM1(FALSE); - rccResetTIM1(); -#if !defined(STM32_TIM1_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM1_UP_NUMBER, STM32_PWM_TIM1_IRQ_PRIORITY); - nvicEnableVector(STM32_TIM1_CC_NUMBER, STM32_PWM_TIM1_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM1CLK) - pwmp->clock = STM32_TIM1CLK; -#else - pwmp->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_PWM_USE_TIM2 - if (&PWMD2 == pwmp) { - rccEnableTIM2(FALSE); - rccResetTIM2(); -#if !defined(STM32_TIM2_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM2_NUMBER, STM32_PWM_TIM2_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM2CLK) - pwmp->clock = STM32_TIM2CLK; -#else - pwmp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_PWM_USE_TIM3 - if (&PWMD3 == pwmp) { - rccEnableTIM3(FALSE); - rccResetTIM3(); -#if !defined(STM32_TIM3_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM3_NUMBER, STM32_PWM_TIM3_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM3CLK) - pwmp->clock = STM32_TIM3CLK; -#else - pwmp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_PWM_USE_TIM4 - if (&PWMD4 == pwmp) { - rccEnableTIM4(FALSE); - rccResetTIM4(); -#if !defined(STM32_TIM4_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM4_NUMBER, STM32_PWM_TIM4_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM4CLK) - pwmp->clock = STM32_TIM4CLK; -#else - pwmp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_PWM_USE_TIM5 - if (&PWMD5 == pwmp) { - rccEnableTIM5(FALSE); - rccResetTIM5(); -#if !defined(STM32_TIM5_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM5_NUMBER, STM32_PWM_TIM5_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM5CLK) - pwmp->clock = STM32_TIM5CLK; -#else - pwmp->clock = STM32_TIMCLK1; -#endif - } -#endif - -#if STM32_PWM_USE_TIM8 - if (&PWMD8 == pwmp) { - rccEnableTIM8(FALSE); - rccResetTIM8(); -#if !defined(STM32_TIM8_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM8_UP_NUMBER, STM32_PWM_TIM8_IRQ_PRIORITY); - nvicEnableVector(STM32_TIM8_CC_NUMBER, STM32_PWM_TIM8_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM8CLK) - pwmp->clock = STM32_TIM8CLK; -#else - pwmp->clock = STM32_TIMCLK2; -#endif - } -#endif - -#if STM32_PWM_USE_TIM9 - if (&PWMD9 == pwmp) { - rccEnableTIM9(FALSE); - rccResetTIM9(); -#if !defined(STM32_TIM9_SUPPRESS_ISR) - nvicEnableVector(STM32_TIM9_NUMBER, STM32_PWM_TIM9_IRQ_PRIORITY); -#endif -#if defined(STM32_TIM9CLK) - pwmp->clock = STM32_TIM9CLK; -#else - pwmp->clock = STM32_TIMCLK2; -#endif - } -#endif - - /* All channels configured in PWM1 mode with preload enabled and will - stay that way until the driver is stopped.*/ - pwmp->tim->CCMR1 = STM32_TIM_CCMR1_OC1M(6) | STM32_TIM_CCMR1_OC1PE | - STM32_TIM_CCMR1_OC2M(6) | STM32_TIM_CCMR1_OC2PE; - pwmp->tim->CCMR2 = STM32_TIM_CCMR2_OC3M(6) | STM32_TIM_CCMR2_OC3PE | - STM32_TIM_CCMR2_OC4M(6) | STM32_TIM_CCMR2_OC4PE; -#if STM32_TIM_MAX_CHANNELS > 4 - pwmp->tim->CCMR3 = STM32_TIM_CCMR3_OC5M(6) | STM32_TIM_CCMR3_OC5PE | - STM32_TIM_CCMR3_OC6M(6) | STM32_TIM_CCMR3_OC6PE; -#endif - } - else { - /* Driver re-configuration scenario, it must be stopped first.*/ - pwmp->tim->CR1 = 0; /* Timer disabled. */ - pwmp->tim->CCR[0] = 0; /* Comparator 1 disabled. */ - pwmp->tim->CCR[1] = 0; /* Comparator 2 disabled. */ - pwmp->tim->CCR[2] = 0; /* Comparator 3 disabled. */ - pwmp->tim->CCR[3] = 0; /* Comparator 4 disabled. */ -#if STM32_TIM_MAX_CHANNELS > 4 - if (pwmp->channels > 4) { - pwmp->tim->CCXR[0] = 0; /* Comparator 5 disabled. */ - pwmp->tim->CCXR[1] = 0; /* Comparator 6 disabled. */ - } -#endif - pwmp->tim->CNT = 0; /* Counter reset to zero. */ - } - - /* Timer configuration.*/ - psc = (pwmp->clock / pwmp->config->frequency) - 1; - osalDbgAssert((psc <= 0xFFFF) && - ((psc + 1) * pwmp->config->frequency) == pwmp->clock, - "invalid frequency"); - pwmp->tim->PSC = psc; - pwmp->tim->ARR = pwmp->period - 1; - pwmp->tim->CR2 = pwmp->config->cr2; - - /* Output enables and polarities setup.*/ - ccer = 0; - switch (pwmp->config->channels[0].mode & PWM_OUTPUT_MASK) { - case PWM_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC1P; - case PWM_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC1E; - default: - ; - } - switch (pwmp->config->channels[1].mode & PWM_OUTPUT_MASK) { - case PWM_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC2P; - case PWM_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC2E; - default: - ; - } - switch (pwmp->config->channels[2].mode & PWM_OUTPUT_MASK) { - case PWM_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC3P; - case PWM_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC3E; - default: - ; - } - switch (pwmp->config->channels[3].mode & PWM_OUTPUT_MASK) { - case PWM_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC4P; - case PWM_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC4E; - default: - ; - } -#if STM32_PWM_USE_ADVANCED -#if STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM8 - if (&PWMD1 == pwmp) { -#endif -#if !STM32_PWM_USE_TIM1 && STM32_PWM_USE_TIM8 - if (&PWMD8 == pwmp) { -#endif -#if STM32_PWM_USE_TIM1 && STM32_PWM_USE_TIM8 - if ((&PWMD1 == pwmp) || (&PWMD8 == pwmp)) { -#endif - switch (pwmp->config->channels[0].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { - case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC1NP; - case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC1NE; - default: - ; - } - switch (pwmp->config->channels[1].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { - case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC2NP; - case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC2NE; - default: - ; - } - switch (pwmp->config->channels[2].mode & PWM_COMPLEMENTARY_OUTPUT_MASK) { - case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW: - ccer |= STM32_TIM_CCER_CC3NP; - case PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH: - ccer |= STM32_TIM_CCER_CC3NE; - default: - ; - } - } -#endif /* STM32_PWM_USE_ADVANCED*/ - - pwmp->tim->CCER = ccer; - pwmp->tim->EGR = STM32_TIM_EGR_UG; /* Update event. */ - pwmp->tim->SR = 0; /* Clear pending IRQs. */ - pwmp->tim->DIER = pwmp->config->dier & /* DMA-related DIER settings. */ - ~STM32_TIM_DIER_IRQ_MASK; -#if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8 -#if STM32_PWM_USE_ADVANCED - pwmp->tim->BDTR = pwmp->config->bdtr | STM32_TIM_BDTR_MOE; -#else - pwmp->tim->BDTR = STM32_TIM_BDTR_MOE; -#endif -#endif - /* Timer configured and started.*/ - pwmp->tim->CR1 = STM32_TIM_CR1_ARPE | STM32_TIM_CR1_URS | - STM32_TIM_CR1_CEN; -} - -/** - * @brief Deactivates the PWM peripheral. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * - * @notapi - */ -void pwm_lld_stop(PWMDriver *pwmp) { - - /* If in ready state then disables the PWM clock.*/ - if (pwmp->state == PWM_READY) { - pwmp->tim->CR1 = 0; /* Timer disabled. */ - pwmp->tim->DIER = 0; /* All IRQs disabled. */ - pwmp->tim->SR = 0; /* Clear eventual pending IRQs. */ -#if STM32_PWM_USE_TIM1 || STM32_PWM_USE_TIM8 - pwmp->tim->BDTR = 0; -#endif - -#if STM32_PWM_USE_TIM1 - if (&PWMD1 == pwmp) { -#if !defined(STM32_TIM1_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM1_UP_NUMBER); - nvicDisableVector(STM32_TIM1_CC_NUMBER); -#endif - rccDisableTIM1(FALSE); - } -#endif - -#if STM32_PWM_USE_TIM2 - if (&PWMD2 == pwmp) { -#if !defined(STM32_TIM2_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM2_NUMBER); -#endif - rccDisableTIM2(FALSE); - } -#endif - -#if STM32_PWM_USE_TIM3 - if (&PWMD3 == pwmp) { -#if !defined(STM32_TIM3_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM3_NUMBER); -#endif - rccDisableTIM3(FALSE); - } -#endif - -#if STM32_PWM_USE_TIM4 - if (&PWMD4 == pwmp) { -#if !defined(STM32_TIM4_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM4_NUMBER); -#endif - rccDisableTIM4(FALSE); - } -#endif - -#if STM32_PWM_USE_TIM5 - if (&PWMD5 == pwmp) { -#if !defined(STM32_TIM5_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM5_NUMBER); -#endif - rccDisableTIM5(FALSE); - } -#endif - -#if STM32_PWM_USE_TIM8 - if (&PWMD8 == pwmp) { -#if !defined(STM32_TIM8_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM8_UP_NUMBER); - nvicDisableVector(STM32_TIM8_CC_NUMBER); -#endif - rccDisableTIM8(FALSE); - } -#endif - -#if STM32_PWM_USE_TIM9 - if (&PWMD9 == pwmp) { -#if !defined(STM32_TIM9_SUPPRESS_ISR) - nvicDisableVector(STM32_TIM9_NUMBER); -#endif - rccDisableTIM9(FALSE); - } -#endif - } -} - -/** - * @brief Enables a PWM channel. - * @pre The PWM unit must have been activated using @p pwmStart(). - * @post The channel is active using the specified configuration. - * @note The function has effect at the next cycle start. - * @note Channel notification is not enabled. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...channels-1) - * @param[in] width PWM pulse width as clock pulses number - * - * @notapi - */ -void pwm_lld_enable_channel(PWMDriver *pwmp, - pwmchannel_t channel, - pwmcnt_t width) { - - /* Changing channel duty cycle on the fly.*/ -#if STM32_TIM_MAX_CHANNELS <= 4 - pwmp->tim->CCR[channel] = width; -#else - if (channel < 4) - pwmp->tim->CCR[channel] = width; - else - pwmp->tim->CCXR[channel - 4] = width; -#endif -} - -/** - * @brief Disables a PWM channel and its notification. - * @pre The PWM unit must have been activated using @p pwmStart(). - * @post The channel is disabled and its output line returned to the - * idle state. - * @note The function has effect at the next cycle start. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...channels-1) - * - * @notapi - */ -void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel) { - -#if STM32_TIM_MAX_CHANNELS <= 4 - pwmp->tim->CCR[channel] = 0; - pwmp->tim->DIER &= ~(2 << channel); -#else - if (channel < 4) { - pwmp->tim->CCR[channel] = 0; - pwmp->tim->DIER &= ~(2 << channel); - } - else - pwmp->tim->CCXR[channel - 4] = 0; -#endif -} - -/** - * @brief Enables the periodic activation edge notification. - * @pre The PWM unit must have been activated using @p pwmStart(). - * @note If the notification is already enabled then the call has no effect. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * - * @notapi - */ -void pwm_lld_enable_periodic_notification(PWMDriver *pwmp) { - uint32_t dier = pwmp->tim->DIER; - - /* If the IRQ is not already enabled care must be taken to clear it, - it is probably already pending because the timer is running.*/ - if ((dier & STM32_TIM_DIER_UIE) == 0) { - pwmp->tim->DIER = dier | STM32_TIM_DIER_UIE; - pwmp->tim->SR &= STM32_TIM_SR_UIF; - } -} - -/** - * @brief Disables the periodic activation edge notification. - * @pre The PWM unit must have been activated using @p pwmStart(). - * @note If the notification is already disabled then the call has no effect. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * - * @notapi - */ -void pwm_lld_disable_periodic_notification(PWMDriver *pwmp) { - - pwmp->tim->DIER &= ~STM32_TIM_DIER_UIE; -} - -/** - * @brief Enables a channel de-activation edge notification. - * @pre The PWM unit must have been activated using @p pwmStart(). - * @pre The channel must have been activated using @p pwmEnableChannel(). - * @note If the notification is already enabled then the call has no effect. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...channels-1) - * - * @notapi - */ -void pwm_lld_enable_channel_notification(PWMDriver *pwmp, - pwmchannel_t channel) { - uint32_t dier = pwmp->tim->DIER; - -#if STM32_TIM_MAX_CHANNELS > 4 - /* Channels 4 and 5 do not support callbacks.*/ - osalDbgAssert(channel < 4, "callback not supported"); -#endif - - /* If the IRQ is not already enabled care must be taken to clear it, - it is probably already pending because the timer is running.*/ - if ((dier & (2 << channel)) == 0) { - pwmp->tim->DIER = dier | (2 << channel); - pwmp->tim->SR = ~(2 << channel); - } -} - -/** - * @brief Disables a channel de-activation edge notification. - * @pre The PWM unit must have been activated using @p pwmStart(). - * @pre The channel must have been activated using @p pwmEnableChannel(). - * @note If the notification is already disabled then the call has no effect. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] channel PWM channel identifier (0...channels-1) - * - * @notapi - */ -void pwm_lld_disable_channel_notification(PWMDriver *pwmp, - pwmchannel_t channel) { - - pwmp->tim->DIER &= ~(2 << channel); -} - -/** - * @brief Common TIM2...TIM5,TIM9 IRQ handler. - * @note It is assumed that the various sources are only activated if the - * associated callback pointer is not equal to @p NULL in order to not - * perform an extra check in a potentially critical interrupt handler. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * - * @notapi - */ -void pwm_lld_serve_interrupt(PWMDriver *pwmp) { - uint32_t sr; - - sr = pwmp->tim->SR; - sr &= pwmp->tim->DIER & STM32_TIM_DIER_IRQ_MASK; - pwmp->tim->SR = ~sr; - if (((sr & STM32_TIM_SR_CC1IF) != 0) && - (pwmp->config->channels[0].callback != NULL)) - pwmp->config->channels[0].callback(pwmp); - if (((sr & STM32_TIM_SR_CC2IF) != 0) && - (pwmp->config->channels[1].callback != NULL)) - pwmp->config->channels[1].callback(pwmp); - if (((sr & STM32_TIM_SR_CC3IF) != 0) && - (pwmp->config->channels[2].callback != NULL)) - pwmp->config->channels[2].callback(pwmp); - if (((sr & STM32_TIM_SR_CC4IF) != 0) && - (pwmp->config->channels[3].callback != NULL)) - pwmp->config->channels[3].callback(pwmp); - if (((sr & STM32_TIM_SR_UIF) != 0) && (pwmp->config->callback != NULL)) - pwmp->config->callback(pwmp); -} - -#endif /* HAL_USE_PWM */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h b/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h deleted file mode 100644 index 981312f38..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/pwm_lld.h +++ /dev/null @@ -1,550 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/pwm_lld.h - * @brief STM32 PWM subsystem low level driver header. - * - * @addtogroup PWM - * @{ - */ - -#ifndef _PWM_LLD_H_ -#define _PWM_LLD_H_ - -#if HAL_USE_PWM || defined(__DOXYGEN__) - -#include "stm32_tim.h" - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Number of PWM channels per PWM driver. - */ -#define PWM_CHANNELS STM32_TIM_MAX_CHANNELS - -/** - * @name STM32-specific PWM complementary output mode macros - * @{ - */ -/** - * @brief Complementary output modes mask. - * @note This is an STM32-specific setting. - */ -#define PWM_COMPLEMENTARY_OUTPUT_MASK 0xF0 - -/** - * @brief Complementary output not driven. - * @note This is an STM32-specific setting. - */ -#define PWM_COMPLEMENTARY_OUTPUT_DISABLED 0x00 - -/** - * @brief Complementary output, active is logic level one. - * @note This is an STM32-specific setting. - * @note This setting is only available if the configuration option - * @p STM32_PWM_USE_ADVANCED is set to TRUE and only for advanced - * timers TIM1 and TIM8. - */ -#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH 0x10 - -/** - * @brief Complementary output, active is logic level zero. - * @note This is an STM32-specific setting. - * @note This setting is only available if the configuration option - * @p STM32_PWM_USE_ADVANCED is set to TRUE and only for advanced - * timers TIM1 and TIM8. - */ -#define PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW 0x20 -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief If advanced timer features switch. - * @details If set to @p TRUE the advanced features for TIM1 and TIM8 are - * enabled. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_ADVANCED) || defined(__DOXYGEN__) -#define STM32_PWM_USE_ADVANCED FALSE -#endif - -/** - * @brief PWMD1 driver enable switch. - * @details If set to @p TRUE the support for PWMD1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM1) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM1 FALSE -#endif - -/** - * @brief PWMD2 driver enable switch. - * @details If set to @p TRUE the support for PWMD2 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM2) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM2 FALSE -#endif - -/** - * @brief PWMD3 driver enable switch. - * @details If set to @p TRUE the support for PWMD3 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM3) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM3 FALSE -#endif - -/** - * @brief PWMD4 driver enable switch. - * @details If set to @p TRUE the support for PWMD4 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM4) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM4 FALSE -#endif - -/** - * @brief PWMD5 driver enable switch. - * @details If set to @p TRUE the support for PWMD5 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM5) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM5 FALSE -#endif - -/** - * @brief PWMD8 driver enable switch. - * @details If set to @p TRUE the support for PWMD8 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM8) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM8 FALSE -#endif - -/** - * @brief PWMD9 driver enable switch. - * @details If set to @p TRUE the support for PWMD9 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_PWM_USE_TIM9) || defined(__DOXYGEN__) -#define STM32_PWM_USE_TIM9 FALSE -#endif - -/** - * @brief PWMD1 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM1_IRQ_PRIORITY 7 -#endif - -/** - * @brief PWMD2 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM2_IRQ_PRIORITY 7 -#endif - -/** - * @brief PWMD3 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM3_IRQ_PRIORITY 7 -#endif - -/** - * @brief PWMD4 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM4_IRQ_PRIORITY 7 -#endif - -/** - * @brief PWMD5 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM5_IRQ_PRIORITY 7 -#endif - -/** - * @brief PWMD8 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM8_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM8_IRQ_PRIORITY 7 -#endif -/** @} */ - -/** - * @brief PWMD9 interrupt priority level setting. - */ -#if !defined(STM32_PWM_TIM9_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_PWM_TIM9_IRQ_PRIORITY 7 -#endif -/** @} */ - -/*===========================================================================*/ -/* Configuration checks. */ -/*===========================================================================*/ - -#if STM32_PWM_USE_TIM1 && !STM32_HAS_TIM1 -#error "TIM1 not present in the selected device" -#endif - -#if STM32_PWM_USE_TIM2 && !STM32_HAS_TIM2 -#error "TIM2 not present in the selected device" -#endif - -#if STM32_PWM_USE_TIM3 && !STM32_HAS_TIM3 -#error "TIM3 not present in the selected device" -#endif - -#if STM32_PWM_USE_TIM4 && !STM32_HAS_TIM4 -#error "TIM4 not present in the selected device" -#endif - -#if STM32_PWM_USE_TIM5 && !STM32_HAS_TIM5 -#error "TIM5 not present in the selected device" -#endif - -#if STM32_PWM_USE_TIM8 && !STM32_HAS_TIM8 -#error "TIM8 not present in the selected device" -#endif - -#if STM32_PWM_USE_TIM9 && !STM32_HAS_TIM9 -#error "TIM9 not present in the selected device" -#endif - -#if !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM2 && \ - !STM32_PWM_USE_TIM3 && !STM32_PWM_USE_TIM4 && \ - !STM32_PWM_USE_TIM5 && !STM32_PWM_USE_TIM8 && \ - !STM32_PWM_USE_TIM9 -#error "PWM driver activated but no TIM peripheral assigned" -#endif - -#if STM32_PWM_USE_ADVANCED && !STM32_PWM_USE_TIM1 && !STM32_PWM_USE_TIM8 -#error "advanced mode selected but no advanced timer assigned" -#endif - -/* Checks on allocation of TIMx units.*/ -#if STM32_PWM_USE_TIM1 -#if defined(STM32_TIM1_IS_USED) -#error "PWMD1 requires TIM1 but the timer is already used" -#else -#define STM32_TIM1_IS_USED -#endif -#endif - -#if STM32_PWM_USE_TIM2 -#if defined(STM32_TIM2_IS_USED) -#error "PWMD2 requires TIM2 but the timer is already used" -#else -#define STM32_TIM2_IS_USED -#endif -#endif - -#if STM32_PWM_USE_TIM3 -#if defined(STM32_TIM3_IS_USED) -#error "PWMD3 requires TIM3 but the timer is already used" -#else -#define STM32_TIM3_IS_USED -#endif -#endif - -#if STM32_PWM_USE_TIM4 -#if defined(STM32_TIM4_IS_USED) -#error "PWMD4 requires TIM4 but the timer is already used" -#else -#define STM32_TIM4_IS_USED -#endif -#endif - -#if STM32_PWM_USE_TIM5 -#if defined(STM32_TIM5_IS_USED) -#error "PWMD5 requires TIM5 but the timer is already used" -#else -#define STM32_TIM5_IS_USED -#endif -#endif - -#if STM32_PWM_USE_TIM8 -#if defined(STM32_TIM8_IS_USED) -#error "PWMD8 requires TIM8 but the timer is already used" -#else -#define STM32_TIM8_IS_USED -#endif -#endif - -#if STM32_PWM_USE_TIM9 -#if defined(STM32_TIM9_IS_USED) -#error "PWMD9 requires TIM9 but the timer is already used" -#else -#define STM32_TIM9_IS_USED -#endif -#endif - -/* IRQ priority checks.*/ -#if STM32_PWM_USE_TIM1 && !defined(STM32_TIM1_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM1" -#endif - -#if STM32_PWM_USE_TIM2 && !defined(STM32_TIM2_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM2" -#endif - -#if STM32_PWM_USE_TIM3 && !defined(STM32_TIM3_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM3" -#endif - -#if STM32_PWM_USE_TIM4 && !defined(STM32_TIM4_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM4" -#endif - -#if STM32_PWM_USE_TIM5 && !defined(STM32_TIM5_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM5" -#endif - -#if STM32_PWM_USE_TIM8 && !defined(STM32_TIM8_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM8_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM8" -#endif - -#if STM32_PWM_USE_TIM9 && !defined(STM32_TIM9_SUPPRESS_ISR) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_PWM_TIM9_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to TIM9" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a PWM mode. - */ -typedef uint32_t pwmmode_t; - -/** - * @brief Type of a PWM channel. - */ -typedef uint8_t pwmchannel_t; - -/** - * @brief Type of a channels mask. - */ -typedef uint32_t pwmchnmsk_t; - -/** - * @brief Type of a PWM counter. - */ -typedef uint32_t pwmcnt_t; - -/** - * @brief Type of a PWM driver channel configuration structure. - */ -typedef struct { - /** - * @brief Channel active logic level. - */ - pwmmode_t mode; - /** - * @brief Channel callback pointer. - * @note This callback is invoked on the channel compare event. If set to - * @p NULL then the callback is disabled. - */ - pwmcallback_t callback; - /* End of the mandatory fields.*/ -} PWMChannelConfig; - -/** - * @brief Type of a PWM driver configuration structure. - */ -typedef struct { - /** - * @brief Timer clock in Hz. - * @note The low level can use assertions in order to catch invalid - * frequency specifications. - */ - uint32_t frequency; - /** - * @brief PWM period in ticks. - * @note The low level can use assertions in order to catch invalid - * period specifications. - */ - pwmcnt_t period; - /** - * @brief Periodic callback pointer. - * @note This callback is invoked on PWM counter reset. If set to - * @p NULL then the callback is disabled. - */ - pwmcallback_t callback; - /** - * @brief Channels configurations. - */ - PWMChannelConfig channels[PWM_CHANNELS]; - /* End of the mandatory fields.*/ - /** - * @brief TIM CR2 register initialization data. - * @note The value of this field should normally be equal to zero. - */ - uint32_t cr2; -#if STM32_PWM_USE_ADVANCED || defined(__DOXYGEN__) - /** - * @brief TIM BDTR (break & dead-time) register initialization data. - * @note The value of this field should normally be equal to zero. - */ \ - uint32_t bdtr; -#endif - /** - * @brief TIM DIER register initialization data. - * @note The value of this field should normally be equal to zero. - * @note Only the DMA-related bits can be specified in this field. - */ - uint32_t dier; -} PWMConfig; - -/** - * @brief Structure representing a PWM driver. - */ -struct PWMDriver { - /** - * @brief Driver state. - */ - pwmstate_t state; - /** - * @brief Current driver configuration data. - */ - const PWMConfig *config; - /** - * @brief Current PWM period in ticks. - */ - pwmcnt_t period; - /** - * @brief Mask of the enabled channels. - */ - pwmchnmsk_t enabled; - /** - * @brief Number of channels in this instance. - */ - pwmchannel_t channels; -#if defined(PWM_DRIVER_EXT_FIELDS) - PWM_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Timer base clock. - */ - uint32_t clock; - /** - * @brief Pointer to the TIMx registers block. - */ - stm32_tim_t *tim; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Changes the period the PWM peripheral. - * @details This function changes the period of a PWM unit that has already - * been activated using @p pwmStart(). - * @pre The PWM unit must have been activated using @p pwmStart(). - * @post The PWM unit period is changed to the new value. - * @note The function has effect at the next cycle start. - * @note If a period is specified that is shorter than the pulse width - * programmed in one of the channels then the behavior is not - * guaranteed. - * - * @param[in] pwmp pointer to a @p PWMDriver object - * @param[in] period new cycle time in ticks - * - * @notapi - */ -#define pwm_lld_change_period(pwmp, period) \ - ((pwmp)->tim->ARR = ((period) - 1)) - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_PWM_USE_TIM1 && !defined(__DOXYGEN__) -extern PWMDriver PWMD1; -#endif - -#if STM32_PWM_USE_TIM2 && !defined(__DOXYGEN__) -extern PWMDriver PWMD2; -#endif - -#if STM32_PWM_USE_TIM3 && !defined(__DOXYGEN__) -extern PWMDriver PWMD3; -#endif - -#if STM32_PWM_USE_TIM4 && !defined(__DOXYGEN__) -extern PWMDriver PWMD4; -#endif - -#if STM32_PWM_USE_TIM5 && !defined(__DOXYGEN__) -extern PWMDriver PWMD5; -#endif - -#if STM32_PWM_USE_TIM8 && !defined(__DOXYGEN__) -extern PWMDriver PWMD8; -#endif - -#if STM32_PWM_USE_TIM9 && !defined(__DOXYGEN__) -extern PWMDriver PWMD9; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void pwm_lld_init(void); - void pwm_lld_start(PWMDriver *pwmp); - void pwm_lld_stop(PWMDriver *pwmp); - void pwm_lld_enable_channel(PWMDriver *pwmp, - pwmchannel_t channel, - pwmcnt_t width); - void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel); - void pwm_lld_enable_periodic_notification(PWMDriver *pwmp); - void pwm_lld_disable_periodic_notification(PWMDriver *pwmp); - void pwm_lld_enable_channel_notification(PWMDriver *pwmp, - pwmchannel_t channel); - void pwm_lld_disable_channel_notification(PWMDriver *pwmp, - pwmchannel_t channel); - void pwm_lld_serve_interrupt(PWMDriver *pwmp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_PWM */ - -#endif /* _PWM_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/st_lld.c b/os/hal/ports/STM32/LLD/TIMv1/st_lld.c deleted file mode 100644 index cea1847ee..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/st_lld.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/st_lld.c - * @brief ST Driver subsystem low level driver code. - * - * @addtogroup ST - * @{ - */ - -#include "hal.h" - -#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING - -#if (OSAL_ST_RESOLUTION == 32) -#define ST_ARR_INIT 0xFFFFFFFF -#else -#define ST_ARR_INIT 0x0000FFFF -#endif - -#if STM32_ST_USE_TIMER == 2 -#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM2_IS_32BITS -#error "TIM2 is not a 32bits timer" -#endif - -#if defined(STM32_TIM2_IS_USED) -#error "ST requires TIM2 but the timer is already used" -#else -#define STM32_TIM2_IS_USED -#endif - -#define ST_HANDLER STM32_TIM2_HANDLER -#define ST_NUMBER STM32_TIM2_NUMBER -#define ST_CLOCK_SRC STM32_TIMCLK1 -#define ST_ENABLE_CLOCK() rccEnableTIM2(FALSE) -#if defined(STM32F1XX) -#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM2_STOP -#elif defined(STM32L4XX) -#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM2_STOP -#else -#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM2_STOP -#endif - -#elif STM32_ST_USE_TIMER == 3 -#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM3_IS_32BITS -#error "TIM3 is not a 32bits timer" -#endif - -#if defined(STM32_TIM3_IS_USED) -#error "ST requires TIM3 but the timer is already used" -#else -#define STM32_TIM3_IS_USED -#endif - -#define ST_HANDLER STM32_TIM3_HANDLER -#define ST_NUMBER STM32_TIM3_NUMBER -#define ST_CLOCK_SRC STM32_TIMCLK1 -#define ST_ENABLE_CLOCK() rccEnableTIM3(FALSE) -#if defined(STM32F1XX) -#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM3_STOP -#elif defined(STM32L4XX) -#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM3_STOP -#else -#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM3_STOP -#endif - -#elif STM32_ST_USE_TIMER == 4 -#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM4_IS_32BITS -#error "TIM4 is not a 32bits timer" -#endif - -#if defined(STM32_TIM4_IS_USED) -#error "ST requires TIM4 but the timer is already used" -#else -#define STM32_TIM4_IS_USED -#endif - -#define ST_HANDLER STM32_TIM4_HANDLER -#define ST_NUMBER STM32_TIM4_NUMBER -#define ST_CLOCK_SRC STM32_TIMCLK1 -#define ST_ENABLE_CLOCK() rccEnableTIM4(FALSE) -#if defined(STM32F1XX) -#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM4_STOP -#elif defined(STM32L4XX) -#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM4_STOP -#else -#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM4_STOP -#endif - -#elif STM32_ST_USE_TIMER == 5 -#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM5_IS_32BITS -#error "TIM5 is not a 32bits timer" -#endif - -#if defined(STM32_TIM5_IS_USED) -#error "ST requires TIM5 but the timer is already used" -#else -#define STM32_TIM5_IS_USED -#endif - -#define ST_HANDLER STM32_TIM5_HANDLER -#define ST_NUMBER STM32_TIM5_NUMBER -#define ST_CLOCK_SRC STM32_TIMCLK1 -#define ST_ENABLE_CLOCK() rccEnableTIM5(FALSE) -#if defined(STM32F1XX) -#define ST_ENABLE_STOP() DBGMCU->CR |= DBGMCU_CR_DBG_TIM5_STOP -#elif defined(STM32L4XX) -#define ST_ENABLE_STOP() DBGMCU->APB1FZR1 |= DBGMCU_APB1FZR1_DBG_TIM5_STOP -#else -#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM5_STOP -#endif - -#elif STM32_ST_USE_TIMER == 21 -#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM21_IS_32BITS -#error "TIM21 is not a 32bits timer" -#endif - -#if defined(STM32_TIM21_IS_USED) -#error "ST requires TIM21 but the timer is already used" -#else -#define STM32_TIM21_IS_USED -#endif - -#define ST_HANDLER STM32_TIM21_HANDLER -#define ST_NUMBER STM32_TIM21_NUMBER -#define ST_CLOCK_SRC STM32_TIMCLK2 -#define ST_ENABLE_CLOCK() rccEnableTIM21(FALSE) -#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB2_FZ_DBG_TIM21_STOP - -#elif STM32_ST_USE_TIMER == 22 -#if (OSAL_ST_RESOLUTION == 32) && !STM32_TIM22_IS_32BITS -#error "TIM21 is not a 32bits timer" -#endif - -#if defined(STM32_TIM22_IS_USED) -#error "ST requires TIM22 but the timer is already used" -#else -#define STM32_TIM22_IS_USED -#endif - -#define ST_HANDLER STM32_TIM22_HANDLER -#define ST_NUMBER STM32_TIM22_NUMBER -#define ST_CLOCK_SRC STM32_TIMCLK2 -#define ST_ENABLE_CLOCK() rccEnableTIM22(FALSE) -#define ST_ENABLE_STOP() DBGMCU->APB1FZ |= DBGMCU_APB2_FZ_DBG_TIM21_STOP - -#else -#error "STM32_ST_USE_TIMER specifies an unsupported timer" -#endif - -#if ST_CLOCK_SRC % OSAL_ST_FREQUENCY != 0 -#error "the selected ST frequency is not obtainable because integer rounding" -#endif - -#if (ST_CLOCK_SRC / OSAL_ST_FREQUENCY) - 1 > 0xFFFF -#error "the selected ST frequency is not obtainable because TIM timer prescaler limits" -#endif - -#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ - -#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC - -#if STM32_HCLK % OSAL_ST_FREQUENCY != 0 -#error "the selected ST frequency is not obtainable because integer rounding" -#endif - -#if (STM32_HCLK / OSAL_ST_FREQUENCY) - 1 > 0xFFFFFF -#error "the selected ST frequency is not obtainable because SysTick timer counter limits" -#endif - -#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if (OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC) || defined(__DOXYGEN__) -/** - * @brief System Timer vector. - * @details This interrupt is used for system tick in periodic mode. - * - * @isr - */ -OSAL_IRQ_HANDLER(SysTick_Handler) { - - OSAL_IRQ_PROLOGUE(); - - osalSysLockFromISR(); - osalOsTimerHandlerI(); - osalSysUnlockFromISR(); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ - -#if (OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING) || defined(__DOXYGEN__) -/** - * @brief TIM2 interrupt handler. - * @details This interrupt is used for system tick in free running mode. - * - * @isr - */ -OSAL_IRQ_HANDLER(ST_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - /* Note, under rare circumstances an interrupt can remain latched even if - the timer SR register has been cleared, in those cases the interrupt - is simply ignored.*/ - if ((STM32_ST_TIM->SR & TIM_SR_CC1IF) != 0U) { - STM32_ST_TIM->SR = 0U; - - osalSysLockFromISR(); - osalOsTimerHandlerI(); - osalSysUnlockFromISR(); - } - - OSAL_IRQ_EPILOGUE(); -} -#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level ST driver initialization. - * - * @notapi - */ -void st_lld_init(void) { - -#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING - /* Free running counter mode.*/ - - /* Enabling timer clock.*/ - ST_ENABLE_CLOCK(); - - /* Enabling the stop mode during debug for this timer.*/ - ST_ENABLE_STOP(); - - /* Initializing the counter in free running mode.*/ - STM32_ST_TIM->PSC = (ST_CLOCK_SRC / OSAL_ST_FREQUENCY) - 1; - STM32_ST_TIM->ARR = ST_ARR_INIT; - STM32_ST_TIM->CCMR1 = 0; - STM32_ST_TIM->CCR[0] = 0; - STM32_ST_TIM->DIER = 0; - STM32_ST_TIM->CR2 = 0; - STM32_ST_TIM->EGR = TIM_EGR_UG; - STM32_ST_TIM->CR1 = TIM_CR1_CEN; - - /* IRQ enabled.*/ - nvicEnableVector(ST_NUMBER, STM32_ST_IRQ_PRIORITY); -#endif /* OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING */ - -#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC - /* Periodic systick mode, the Cortex-Mx internal systick timer is used - in this mode.*/ - SysTick->LOAD = (STM32_HCLK / OSAL_ST_FREQUENCY) - 1; - SysTick->VAL = 0; - SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | - SysTick_CTRL_ENABLE_Msk | - SysTick_CTRL_TICKINT_Msk; - - /* IRQ enabled.*/ - nvicSetSystemHandlerPriority(HANDLER_SYSTICK, STM32_ST_IRQ_PRIORITY); -#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */ -} - -#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/TIMv1/st_lld.h b/os/hal/ports/STM32/LLD/TIMv1/st_lld.h deleted file mode 100644 index beefd16e2..000000000 --- a/os/hal/ports/STM32/LLD/TIMv1/st_lld.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/st_lld.h - * @brief ST Driver subsystem low level driver header. - * @details This header is designed to be include-able without having to - * include other files from the HAL. - * - * @addtogroup ST - * @{ - */ - -#ifndef _ST_LLD_H_ -#define _ST_LLD_H_ - -#include "mcuconf.h" -#include "stm32_registry.h" -#include "stm32_tim.h" - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief SysTick timer IRQ priority. - */ -#if !defined(STM32_ST_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_ST_IRQ_PRIORITY 8 -#endif - -/** - * @brief TIMx unit (by number) to be used for free running operations. - * @note You must select a 32 bits timer if a 32 bits @p systick_t type - * is required. - * @note Timers 2, 3, 4 and 5 are supported. - */ -#if !defined(STM32_ST_USE_TIMER) || defined(__DOXYGEN__) -#define STM32_ST_USE_TIMER 2 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_ST_USE_TIMER == 2 -#if !STM32_HAS_TIM2 -#error "TIM2 not present" -#endif -#define STM32_ST_TIM STM32_TIM2 - -#elif STM32_ST_USE_TIMER == 3 -#if !STM32_HAS_TIM3 -#error "TIM3 not present" -#endif -#define STM32_ST_TIM STM32_TIM3 - -#elif STM32_ST_USE_TIMER == 4 -#if !STM32_HAS_TIM4 -#error "TIM4 not present" -#endif -#define STM32_ST_TIM STM32_TIM4 - -#elif STM32_ST_USE_TIMER == 5 -#if !STM32_HAS_TIM5 -#error "TIM5 not present" -#endif -#define STM32_ST_TIM STM32_TIM5 - -#elif STM32_ST_USE_TIMER == 21 -#if !STM32_HAS_TIM21 -#error "TIM21 not present" -#endif -#define STM32_ST_TIM STM32_TIM21 - -#elif STM32_ST_USE_TIMER == 22 -#if !STM32_HAS_TIM22 -#error "TIM22 not present" -#endif -#define STM32_ST_TIM STM32_TIM22 - -#else -#error "STM32_ST_USE_TIMER specifies an unsupported timer" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#ifdef __cplusplus -extern "C" { -#endif - void st_lld_init(void); -#ifdef __cplusplus -} -#endif - -/*===========================================================================*/ -/* Driver inline functions. */ -/*===========================================================================*/ - -/** - * @brief Returns the time counter value. - * - * @return The counter value. - * - * @notapi - */ -static inline systime_t st_lld_get_counter(void) { - - return (systime_t)STM32_ST_TIM->CNT; -} - -/** - * @brief Starts the alarm. - * @note Makes sure that no spurious alarms are triggered after - * this call. - * - * @param[in] time the time to be set for the first alarm - * - * @notapi - */ -static inline void st_lld_start_alarm(systime_t time) { - - STM32_ST_TIM->CCR[0] = (uint32_t)time; - STM32_ST_TIM->SR = 0; - STM32_ST_TIM->DIER = STM32_TIM_DIER_CC1IE; -} - -/** - * @brief Stops the alarm interrupt. - * - * @notapi - */ -static inline void st_lld_stop_alarm(void) { - - STM32_ST_TIM->DIER = 0; -} - -/** - * @brief Sets the alarm time. - * - * @param[in] time the time to be set for the next alarm - * - * @notapi - */ -static inline void st_lld_set_alarm(systime_t time) { - - STM32_ST_TIM->CCR[0] = (uint32_t)time; -} - -/** - * @brief Returns the current alarm time. - * - * @return The currently set alarm time. - * - * @notapi - */ -static inline systime_t st_lld_get_alarm(void) { - - return (systime_t)STM32_ST_TIM->CCR[0]; -} - -/** - * @brief Determines if the alarm is active. - * - * @return The alarm status. - * @retval false if the alarm is not active. - * @retval true is the alarm is active - * - * @notapi - */ -static inline bool st_lld_is_alarm_active(void) { - - return (bool)((STM32_ST_TIM->DIER & STM32_TIM_DIER_CC1IE) != 0); -} - -#endif /* _ST_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.c b/os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.c new file mode 100644 index 000000000..c352b054f --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.c @@ -0,0 +1,631 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv1/serial_lld.c + * @brief STM32 low level serial driver code. + * + * @addtogroup SERIAL + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SERIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USART1 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) +SerialDriver SD1; +#endif + +/** @brief USART2 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) +SerialDriver SD2; +#endif + +/** @brief USART3 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) +SerialDriver SD3; +#endif + +/** @brief UART4 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) +SerialDriver SD4; +#endif + +/** @brief UART5 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) +SerialDriver SD5; +#endif + +/** @brief USART6 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) +SerialDriver SD6; +#endif + +/** @brief UART7 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) +SerialDriver SD7; +#endif + +/** @brief UART8 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +SerialDriver SD8; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** @brief Driver default configuration.*/ +static const SerialConfig default_config = +{ + SERIAL_DEFAULT_BITRATE, + 0, + USART_CR2_STOP1_BITS | USART_CR2_LINEN, + 0 +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief USART initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration + */ +static void usart_init(SerialDriver *sdp, const SerialConfig *config) { + USART_TypeDef *u = sdp->usart; + + /* Baud rate setting.*/ +#if STM32_HAS_USART6 + if ((sdp->usart == USART1) || (sdp->usart == USART6)) +#else + if (sdp->usart == USART1) +#endif + u->BRR = STM32_PCLK2 / config->speed; + else + u->BRR = STM32_PCLK1 / config->speed; + + /* Note that some bits are enforced.*/ + u->CR2 = config->cr2 | USART_CR2_LBDIE; + u->CR3 = config->cr3 | USART_CR3_EIE; + u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE | + USART_CR1_RXNEIE | USART_CR1_TE | + USART_CR1_RE; + u->SR = 0; + (void)u->SR; /* SR reset step 1.*/ + (void)u->DR; /* SR reset step 2.*/ +} + +/** + * @brief USART de-initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] u pointer to an USART I/O block + */ +static void usart_deinit(USART_TypeDef *u) { + + u->CR1 = 0; + u->CR2 = 0; + u->CR3 = 0; +} + +/** + * @brief Error handling routine. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] sr USART SR register value + */ +static void set_error(SerialDriver *sdp, uint16_t sr) { + eventflags_t sts = 0; + + if (sr & USART_SR_ORE) + sts |= SD_OVERRUN_ERROR; + if (sr & USART_SR_PE) + sts |= SD_PARITY_ERROR; + if (sr & USART_SR_FE) + sts |= SD_FRAMING_ERROR; + if (sr & USART_SR_NE) + sts |= SD_NOISE_ERROR; + chnAddFlagsI(sdp, sts); +} + +/** + * @brief Common IRQ handler. + * + * @param[in] sdp communication channel associated to the USART + */ +static void serve_interrupt(SerialDriver *sdp) { + USART_TypeDef *u = sdp->usart; + uint16_t cr1 = u->CR1; + uint16_t sr = u->SR; + + /* Special case, LIN break detection.*/ + if (sr & USART_SR_LBD) { + osalSysLockFromISR(); + chnAddFlagsI(sdp, SD_BREAK_DETECTED); + u->SR = ~USART_SR_LBD; + osalSysUnlockFromISR(); + } + + /* Data available.*/ + osalSysLockFromISR(); + while (sr & (USART_SR_RXNE | USART_SR_ORE | USART_SR_NE | USART_SR_FE | + USART_SR_PE)) { + uint8_t b; + + /* Error condition detection.*/ + if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) + set_error(sdp, sr); + b = u->DR; + if (sr & USART_SR_RXNE) + sdIncomingDataI(sdp, b); + sr = u->SR; + } + osalSysUnlockFromISR(); + + /* Transmission buffer empty.*/ + if ((cr1 & USART_CR1_TXEIE) && (sr & USART_SR_TXE)) { + msg_t b; + osalSysLockFromISR(); + b = oqGetI(&sdp->oqueue); + if (b < MSG_OK) { + chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); + u->CR1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; + } + else + u->DR = b; + osalSysUnlockFromISR(); + } + + /* Physical transmission end.*/ + if (sr & USART_SR_TC) { + osalSysLockFromISR(); + if (oqIsEmptyI(&sdp->oqueue)) + chnAddFlagsI(sdp, CHN_TRANSMISSION_END); + u->CR1 = cr1 & ~USART_CR1_TCIE; + u->SR = ~USART_SR_TC; + osalSysUnlockFromISR(); + } +} + +#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) +static void notify1(io_queue_t *qp) { + + (void)qp; + USART1->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) +static void notify2(io_queue_t *qp) { + + (void)qp; + USART2->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) +static void notify3(io_queue_t *qp) { + + (void)qp; + USART3->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) +static void notify4(io_queue_t *qp) { + + (void)qp; + UART4->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) +static void notify5(io_queue_t *qp) { + + (void)qp; + UART5->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) +static void notify6(io_queue_t *qp) { + + (void)qp; + USART6->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) +static void notify7(io_queue_t *qp) { + + (void)qp; + UART7->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +static void notify8(io_queue_t *qp) { + + (void)qp; + UART8->CR1 |= USART_CR1_TXEIE; +} +#endif + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) +#if !defined(STM32_USART1_HANDLER) +#error "STM32_USART1_HANDLER not defined" +#endif +/** + * @brief USART1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) +#if !defined(STM32_USART2_HANDLER) +#error "STM32_USART2_HANDLER not defined" +#endif +/** + * @brief USART2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) +#if !defined(STM32_USART3_HANDLER) +#error "STM32_USART3_HANDLER not defined" +#endif +/** + * @brief USART3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) +#if !defined(STM32_UART4_HANDLER) +#error "STM32_UART4_HANDLER not defined" +#endif +/** + * @brief UART4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) +#if !defined(STM32_UART5_HANDLER) +#error "STM32_UART5_HANDLER not defined" +#endif +/** + * @brief UART5 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) +#if !defined(STM32_USART6_HANDLER) +#error "STM32_USART6_HANDLER not defined" +#endif +/** + * @brief USART6 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD6); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) +#if !defined(STM32_UART7_HANDLER) +#error "STM32_UART7_HANDLER not defined" +#endif +/** + * @brief UART7 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART7_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD7); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +#if !defined(STM32_UART8_HANDLER) +#error "STM32_UART8_HANDLER not defined" +#endif +/** + * @brief UART8 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART8_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD8); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level serial driver initialization. + * + * @notapi + */ +void sd_lld_init(void) { + +#if STM32_SERIAL_USE_USART1 + sdObjectInit(&SD1, NULL, notify1); + SD1.usart = USART1; +#endif + +#if STM32_SERIAL_USE_USART2 + sdObjectInit(&SD2, NULL, notify2); + SD2.usart = USART2; +#endif + +#if STM32_SERIAL_USE_USART3 + sdObjectInit(&SD3, NULL, notify3); + SD3.usart = USART3; +#endif + +#if STM32_SERIAL_USE_UART4 + sdObjectInit(&SD4, NULL, notify4); + SD4.usart = UART4; +#endif + +#if STM32_SERIAL_USE_UART5 + sdObjectInit(&SD5, NULL, notify5); + SD5.usart = UART5; +#endif + +#if STM32_SERIAL_USE_USART6 + sdObjectInit(&SD6, NULL, notify6); + SD6.usart = USART6; +#endif + +#if STM32_SERIAL_USE_UART7 + sdObjectInit(&SD7, NULL, notify7); + SD7.usart = UART7; +#endif + +#if STM32_SERIAL_USE_UART8 + sdObjectInit(&SD8, NULL, notify8); + SD8.usart = UART8; +#endif +} + +/** + * @brief Low level serial driver configuration and (re)start. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration. + * If this parameter is set to @p NULL then a default + * configuration is used. + * + * @notapi + */ +void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { + + if (config == NULL) + config = &default_config; + + if (sdp->state == SD_STOP) { +#if STM32_SERIAL_USE_USART1 + if (&SD1 == sdp) { + rccEnableUSART1(FALSE); + nvicEnableVector(STM32_USART1_NUMBER, STM32_SERIAL_USART1_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_USART2 + if (&SD2 == sdp) { + rccEnableUSART2(FALSE); + nvicEnableVector(STM32_USART2_NUMBER, STM32_SERIAL_USART2_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_USART3 + if (&SD3 == sdp) { + rccEnableUSART3(FALSE); + nvicEnableVector(STM32_USART3_NUMBER, STM32_SERIAL_USART3_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_UART4 + if (&SD4 == sdp) { + rccEnableUART4(FALSE); + nvicEnableVector(STM32_UART4_NUMBER, STM32_SERIAL_UART4_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_UART5 + if (&SD5 == sdp) { + rccEnableUART5(FALSE); + nvicEnableVector(STM32_UART5_NUMBER, STM32_SERIAL_UART5_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_USART6 + if (&SD6 == sdp) { + rccEnableUSART6(FALSE); + nvicEnableVector(STM32_USART6_NUMBER, STM32_SERIAL_USART6_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_UART7 + if (&SD7 == sdp) { + rccEnableUART7(FALSE); + nvicEnableVector(STM32_UART7_NUMBER, STM32_SERIAL_UART7_PRIORITY); + } +#endif +#if STM32_SERIAL_USE_UART8 + if (&SD8 == sdp) { + rccEnableUART8(FALSE); + nvicEnableVector(STM32_UART8_NUMBER, STM32_SERIAL_UART8_PRIORITY); + } +#endif + } + usart_init(sdp, config); +} + +/** + * @brief Low level serial driver stop. + * @details De-initializes the USART, stops the associated clock, resets the + * interrupt vector. + * + * @param[in] sdp pointer to a @p SerialDriver object + * + * @notapi + */ +void sd_lld_stop(SerialDriver *sdp) { + + if (sdp->state == SD_READY) { + usart_deinit(sdp->usart); +#if STM32_SERIAL_USE_USART1 + if (&SD1 == sdp) { + rccDisableUSART1(FALSE); + nvicDisableVector(STM32_USART1_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_USART2 + if (&SD2 == sdp) { + rccDisableUSART2(FALSE); + nvicDisableVector(STM32_USART2_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_USART3 + if (&SD3 == sdp) { + rccDisableUSART3(FALSE); + nvicDisableVector(STM32_USART3_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_UART4 + if (&SD4 == sdp) { + rccDisableUART4(FALSE); + nvicDisableVector(STM32_UART4_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_UART5 + if (&SD5 == sdp) { + rccDisableUART5(FALSE); + nvicDisableVector(STM32_UART5_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_USART6 + if (&SD6 == sdp) { + rccDisableUSART6(FALSE); + nvicDisableVector(STM32_USART6_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_UART7 + if (&SD7 == sdp) { + rccDisableUART7(FALSE); + nvicDisableVector(STM32_UART7_NUMBER); + return; + } +#endif +#if STM32_SERIAL_USE_UART8 + if (&SD8 == sdp) { + rccDisableUART8(FALSE); + nvicDisableVector(STM32_UART8_NUMBER); + return; + } +#endif + } +} + +#endif /* HAL_USE_SERIAL */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.h b/os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.h new file mode 100644 index 000000000..0e76089b5 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv1/hal_serial_lld.h @@ -0,0 +1,360 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv1/serial_lld.h + * @brief STM32 low level serial driver header. + * + * @addtogroup SERIAL + * @{ + */ + +#ifndef _SERIAL_LLD_H_ +#define _SERIAL_LLD_H_ + +#if HAL_USE_SERIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief USART1 driver enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART1) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART1 FALSE +#endif + +/** + * @brief USART2 driver enable switch. + * @details If set to @p TRUE the support for USART2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART2) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART2 FALSE +#endif + +/** + * @brief USART3 driver enable switch. + * @details If set to @p TRUE the support for USART3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART3) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART3 FALSE +#endif + +/** + * @brief UART4 driver enable switch. + * @details If set to @p TRUE the support for UART4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART4) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART4 FALSE +#endif + +/** + * @brief UART5 driver enable switch. + * @details If set to @p TRUE the support for UART5 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART5) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART5 FALSE +#endif + +/** + * @brief USART6 driver enable switch. + * @details If set to @p TRUE the support for USART6 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART6) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART6 FALSE +#endif + +/** + * @brief UART7 driver enable switch. + * @details If set to @p TRUE the support for UART7 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART7) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART7 FALSE +#endif + +/** + * @brief UART8 driver enable switch. + * @details If set to @p TRUE the support for UART8 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART8) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART8 FALSE +#endif + +/** + * @brief USART1 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART1_PRIORITY 12 +#endif + +/** + * @brief USART2 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART2_PRIORITY 12 +#endif + +/** + * @brief USART3 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART3_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART3_PRIORITY 12 +#endif + +/** + * @brief UART4 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART4_PRIORITY 12 +#endif + +/** + * @brief UART5 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART5_PRIORITY 12 +#endif + +/** + * @brief USART6 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART6_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART6_PRIORITY 12 +#endif + +/** + * @brief UART7 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART7_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART7_PRIORITY 12 +#endif + +/** + * @brief UART8 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART8_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART8_PRIORITY 12 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_SERIAL_USE_USART1 && !STM32_HAS_USART1 +#error "USART1 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_USART2 && !STM32_HAS_USART2 +#error "USART2 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_USART3 && !STM32_HAS_USART3 +#error "USART3 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART4 && !STM32_HAS_UART4 +#error "UART4 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART5 && !STM32_HAS_UART5 +#error "UART5 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_USART6 && !STM32_HAS_USART6 +#error "USART6 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART7 && !STM32_HAS_UART7 +#error "UART7 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART8 && !STM32_HAS_UART8 +#error "UART8 not present in the selected device" +#endif + +#if !STM32_SERIAL_USE_USART1 && !STM32_SERIAL_USE_USART2 && \ + !STM32_SERIAL_USE_USART3 && !STM32_SERIAL_USE_UART4 && \ + !STM32_SERIAL_USE_UART5 && !STM32_SERIAL_USE_USART6 && \ + !STM32_SERIAL_USE_UART7 && !STM32_SERIAL_USE_UART8 +#error "SERIAL driver activated but no USART/UART peripheral assigned" +#endif + +#if STM32_SERIAL_USE_USART1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART1_PRIORITY) +#error "Invalid IRQ priority assigned to USART1" +#endif + +#if STM32_SERIAL_USE_USART2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART2_PRIORITY) +#error "Invalid IRQ priority assigned to USART2" +#endif + +#if STM32_SERIAL_USE_USART3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART3_PRIORITY) +#error "Invalid IRQ priority assigned to USART3" +#endif + +#if STM32_SERIAL_USE_UART4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART4_PRIORITY) +#error "Invalid IRQ priority assigned to UART4" +#endif + +#if STM32_SERIAL_USE_UART5 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART5_PRIORITY) +#error "Invalid IRQ priority assigned to UART5" +#endif + +#if STM32_SERIAL_USE_USART6 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART6_PRIORITY) +#error "Invalid IRQ priority assigned to USART6" +#endif + +#if STM32_SERIAL_USE_UART7 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART7_PRIORITY) +#error "Invalid IRQ priority assigned to UART7" +#endif + +#if STM32_SERIAL_USE_UART8 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART8_PRIORITY) +#error "Invalid IRQ priority assigned to UART8" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief STM32 Serial Driver configuration structure. + * @details An instance of this structure must be passed to @p sdStart() + * in order to configure and start a serial driver operations. + * @note This structure content is architecture dependent, each driver + * implementation defines its own version and the custom static + * initializers. + */ +typedef struct { + /** + * @brief Bit rate. + */ + uint32_t speed; + /* End of the mandatory fields.*/ + /** + * @brief Initialization value for the CR1 register. + */ + uint16_t cr1; + /** + * @brief Initialization value for the CR2 register. + */ + uint16_t cr2; + /** + * @brief Initialization value for the CR3 register. + */ + uint16_t cr3; +} SerialConfig; + +/** + * @brief @p SerialDriver specific data. + */ +#define _serial_driver_data \ + _base_asynchronous_channel_data \ + /* Driver state.*/ \ + sdstate_t state; \ + /* Input queue.*/ \ + input_queue_t iqueue; \ + /* Output queue.*/ \ + output_queue_t oqueue; \ + /* Input circular buffer.*/ \ + uint8_t ib[SERIAL_BUFFERS_SIZE]; \ + /* Output circular buffer.*/ \ + uint8_t ob[SERIAL_BUFFERS_SIZE]; \ + /* End of the mandatory fields.*/ \ + /* Pointer to the USART registers block.*/ \ + USART_TypeDef *usart; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/* + * Extra USARTs definitions here (missing from the ST header file). + */ +#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/ +#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/ +#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/ +#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_SERIAL_USE_USART1 && !defined(__DOXYGEN__) +extern SerialDriver SD1; +#endif +#if STM32_SERIAL_USE_USART2 && !defined(__DOXYGEN__) +extern SerialDriver SD2; +#endif +#if STM32_SERIAL_USE_USART3 && !defined(__DOXYGEN__) +extern SerialDriver SD3; +#endif +#if STM32_SERIAL_USE_UART4 && !defined(__DOXYGEN__) +extern SerialDriver SD4; +#endif +#if STM32_SERIAL_USE_UART5 && !defined(__DOXYGEN__) +extern SerialDriver SD5; +#endif +#if STM32_SERIAL_USE_USART6 && !defined(__DOXYGEN__) +extern SerialDriver SD6; +#endif +#if STM32_SERIAL_USE_UART7 && !defined(__DOXYGEN__) +extern SerialDriver SD7; +#endif +#if STM32_SERIAL_USE_UART8 && !defined(__DOXYGEN__) +extern SerialDriver SD8; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sd_lld_init(void); + void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); + void sd_lld_stop(SerialDriver *sdp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SERIAL */ + +#endif /* _SERIAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c b/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c new file mode 100644 index 000000000..9019d83e6 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.c @@ -0,0 +1,811 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv1/uart_lld.c + * @brief STM32 low level UART driver code. + * + * @addtogroup UART + * @{ + */ + +#include "hal.h" + +#if HAL_USE_UART || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define USART1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART1_RX_DMA_STREAM, \ + STM32_USART1_RX_DMA_CHN) + +#define USART1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART1_TX_DMA_STREAM, \ + STM32_USART1_TX_DMA_CHN) + +#define USART2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART2_RX_DMA_STREAM, \ + STM32_USART2_RX_DMA_CHN) + +#define USART2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART2_TX_DMA_STREAM, \ + STM32_USART2_TX_DMA_CHN) + +#define USART3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART3_RX_DMA_STREAM, \ + STM32_USART3_RX_DMA_CHN) + +#define USART3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART3_TX_DMA_STREAM, \ + STM32_USART3_TX_DMA_CHN) + +#define UART4_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART4_RX_DMA_STREAM, \ + STM32_UART4_RX_DMA_CHN) + +#define UART4_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART4_TX_DMA_STREAM, \ + STM32_UART4_TX_DMA_CHN) + +#define UART5_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART5_RX_DMA_STREAM, \ + STM32_UART5_RX_DMA_CHN) + +#define UART5_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART5_TX_DMA_STREAM, \ + STM32_UART5_TX_DMA_CHN) + +#define USART6_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART6_RX_DMA_STREAM, \ + STM32_USART6_RX_DMA_CHN) + +#define USART6_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART6_TX_DMA_STREAM, \ + STM32_USART6_TX_DMA_CHN) + +#define STM32_UART45_CR2_CHECK_MASK \ + (USART_CR2_STOP_0 | USART_CR2_CLKEN | USART_CR2_CPOL | USART_CR2_CPHA | \ + USART_CR2_LBCL) + +#define STM32_UART45_CR3_CHECK_MASK \ + (USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE | USART_CR3_SCEN | \ + USART_CR3_NACK) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USART1 UART driver identifier.*/ +#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) +UARTDriver UARTD1; +#endif + +/** @brief USART2 UART driver identifier.*/ +#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) +UARTDriver UARTD2; +#endif + +/** @brief USART3 UART driver identifier.*/ +#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) +UARTDriver UARTD3; +#endif + +/** @brief UART4 UART driver identifier.*/ +#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) +UARTDriver UARTD4; +#endif + +/** @brief UART5 UART driver identifier.*/ +#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) +UARTDriver UARTD5; +#endif + +/** @brief USART6 UART driver identifier.*/ +#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) +UARTDriver UARTD6; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Status bits translation. + * + * @param[in] sr USART SR register value + * + * @return The error flags. + */ +static uartflags_t translate_errors(uint16_t sr) { + uartflags_t sts = 0; + + if (sr & USART_SR_ORE) + sts |= UART_OVERRUN_ERROR; + if (sr & USART_SR_PE) + sts |= UART_PARITY_ERROR; + if (sr & USART_SR_FE) + sts |= UART_FRAMING_ERROR; + if (sr & USART_SR_NE) + sts |= UART_NOISE_ERROR; + if (sr & USART_SR_LBD) + sts |= UART_BREAK_DETECTED; + return sts; +} + +/** + * @brief Puts the receiver in the UART_RX_IDLE state. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void uart_enter_rx_idle_loop(UARTDriver *uartp) { + uint32_t mode; + + /* RX DMA channel preparation, if the char callback is defined then the + TCIE interrupt is enabled too.*/ + if (uartp->config->rxchar_cb == NULL) + mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC; + else + mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE; + dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf); + dmaStreamSetTransactionSize(uartp->dmarx, 1); + dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode); + dmaStreamEnable(uartp->dmarx); +} + +/** + * @brief USART de-initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void usart_stop(UARTDriver *uartp) { + + /* Stops RX and TX DMA channels.*/ + dmaStreamDisable(uartp->dmarx); + dmaStreamDisable(uartp->dmatx); + + /* Stops USART operations.*/ + uartp->usart->CR1 = 0; + uartp->usart->CR2 = 0; + uartp->usart->CR3 = 0; +} + +/** + * @brief USART initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void usart_start(UARTDriver *uartp) { + uint16_t cr1; + USART_TypeDef *u = uartp->usart; + + /* Defensive programming, starting from a clean state.*/ + usart_stop(uartp); + + /* Baud rate setting.*/ +#if STM32_HAS_USART6 + if ((uartp->usart == USART1) || (uartp->usart == USART6)) +#else + if (uartp->usart == USART1) +#endif + u->BRR = STM32_PCLK2 / uartp->config->speed; + else + u->BRR = STM32_PCLK1 / uartp->config->speed; + + /* Resetting eventual pending status flags.*/ + (void)u->SR; /* SR reset step 1.*/ + (void)u->DR; /* SR reset step 2.*/ + u->SR = 0; + + /* Note that some bits are enforced because required for correct driver + operations.*/ + u->CR2 = uartp->config->cr2 | USART_CR2_LBDIE; + u->CR3 = uartp->config->cr3 | USART_CR3_DMAT | USART_CR3_DMAR | + USART_CR3_EIE; + + /* Mustn't ever set TCIE here - if done, it causes an immediate + interrupt.*/ + cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE; + u->CR1 = uartp->config->cr1 | cr1; + + /* Starting the receiver idle loop.*/ + uart_enter_rx_idle_loop(uartp); +} + +/** + * @brief RX DMA common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_UART_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_UART_DMA_ERROR_HOOK(uartp); + } +#else + (void)flags; +#endif + + if (uartp->rxstate == UART_RX_IDLE) { + /* Receiver in idle state, a callback is generated, if enabled, for each + received character and then the driver stays in the same state.*/ + _uart_rx_idle_code(uartp); + } + else { + /* Receiver in active state, a callback is generated, if enabled, after + a completed transfer.*/ + dmaStreamDisable(uartp->dmarx); + _uart_rx_complete_isr_code(uartp); + } +} + +/** + * @brief TX DMA common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_UART_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_UART_DMA_ERROR_HOOK(uartp); + } +#else + (void)flags; +#endif + + dmaStreamDisable(uartp->dmatx); + + /* A callback is generated, if enabled, after a completed transfer.*/ + _uart_tx1_isr_code(uartp); +} + +/** + * @brief USART common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void serve_usart_irq(UARTDriver *uartp) { + uint16_t sr; + USART_TypeDef *u = uartp->usart; + uint32_t cr1 = u->CR1; + + sr = u->SR; /* SR reset step 1.*/ + (void)u->DR; /* SR reset step 2.*/ + + if (sr & (USART_SR_LBD | USART_SR_ORE | USART_SR_NE | + USART_SR_FE | USART_SR_PE)) { + u->SR = ~USART_SR_LBD; + _uart_rx_error_isr_code(uartp, translate_errors(sr)); + } + + if ((sr & USART_SR_TC) && (cr1 & USART_CR1_TCIE)) { + /* TC interrupt cleared and disabled.*/ + u->SR = ~USART_SR_TC; + u->CR1 = cr1 & ~USART_CR1_TCIE; + + /* End of transmission, a callback is generated.*/ + _uart_tx2_isr_code(uartp); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) +#if !defined(STM32_USART1_HANDLER) +#error "STM32_USART1_HANDLER not defined" +#endif +/** + * @brief USART1 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART1 */ + +#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) +#if !defined(STM32_USART2_HANDLER) +#error "STM32_USART2_HANDLER not defined" +#endif +/** + * @brief USART2 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART2 */ + +#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) +#if !defined(STM32_USART3_HANDLER) +#error "STM32_USART3_HANDLER not defined" +#endif +/** + * @brief USART3 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART3 */ + +#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) +#if !defined(STM32_UART4_HANDLER) +#error "STM32_UART4_HANDLER not defined" +#endif +/** + * @brief UART4 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_UART4 */ + +#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) +#if !defined(STM32_UART5_HANDLER) +#error "STM32_UART5_HANDLER not defined" +#endif +/** + * @brief UART5 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_UART5 */ + +#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) +#if !defined(STM32_USART6_HANDLER) +#error "STM32_USART6_HANDLER not defined" +#endif +/** + * @brief USART6 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD6); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART6 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level UART driver initialization. + * + * @notapi + */ +void uart_lld_init(void) { + +#if STM32_UART_USE_USART1 + uartObjectInit(&UARTD1); + UARTD1.usart = USART1; + UARTD1.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD1.dmarx = STM32_DMA_STREAM(STM32_UART_USART1_RX_DMA_STREAM); + UARTD1.dmatx = STM32_DMA_STREAM(STM32_UART_USART1_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_USART2 + uartObjectInit(&UARTD2); + UARTD2.usart = USART2; + UARTD2.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD2.dmarx = STM32_DMA_STREAM(STM32_UART_USART2_RX_DMA_STREAM); + UARTD2.dmatx = STM32_DMA_STREAM(STM32_UART_USART2_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_USART3 + uartObjectInit(&UARTD3); + UARTD3.usart = USART3; + UARTD3.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD3.dmarx = STM32_DMA_STREAM(STM32_UART_USART3_RX_DMA_STREAM); + UARTD3.dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_UART4 + uartObjectInit(&UARTD4); + UARTD4.usart = UART4; + UARTD4.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD4.dmarx = STM32_DMA_STREAM(STM32_UART_UART4_RX_DMA_STREAM); + UARTD4.dmatx = STM32_DMA_STREAM(STM32_UART_UART4_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_UART5 + uartObjectInit(&UARTD5); + UARTD5.usart = UART5; + UARTD5.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD5.dmarx = STM32_DMA_STREAM(STM32_UART_UART5_RX_DMA_STREAM); + UARTD5.dmatx = STM32_DMA_STREAM(STM32_UART_UART5_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_USART6 + uartObjectInit(&UARTD6); + UARTD6.usart = USART6; + UARTD6.dmarx = STM32_DMA_STREAM(STM32_UART_USART6_RX_DMA_STREAM); + UARTD6.dmatx = STM32_DMA_STREAM(STM32_UART_USART6_TX_DMA_STREAM); +#endif +} + +/** + * @brief Configures and activates the UART peripheral. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +void uart_lld_start(UARTDriver *uartp) { + + if (uartp->state == UART_STOP) { +#if STM32_UART_USE_USART1 + if (&UARTD1 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART1_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART1_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART1(FALSE); + nvicEnableVector(STM32_USART1_NUMBER, STM32_UART_USART1_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART1_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_USART2 + if (&UARTD2 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART2_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART2_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART2(FALSE); + nvicEnableVector(STM32_USART2_NUMBER, STM32_UART_USART2_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART2_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_USART3 + if (&UARTD3 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART3_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART3_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART3(FALSE); + nvicEnableVector(STM32_USART3_NUMBER, STM32_UART_USART3_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_UART4 + if (&UARTD4 == uartp) { + bool b; + + chDbgAssert((uartp->config->cr2 & STM32_UART45_CR2_CHECK_MASK) == 0, + "specified invalid bits in UART4 CR2 register settings"); + chDbgAssert((uartp->config->cr3 & STM32_UART45_CR3_CHECK_MASK) == 0, + "specified invalid bits in UART4 CR3 register settings"); + + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_UART4_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_UART4_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUART4(FALSE); + nvicEnableVector(STM32_UART4_NUMBER, STM32_UART_UART4_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(UART4_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_UART4_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_UART5 + if (&UARTD5 == uartp) { + bool b; + + chDbgAssert((uartp->config->cr2 & STM32_UART45_CR2_CHECK_MASK) == 0, + "specified invalid bits in UART5 CR2 register settings"); + chDbgAssert((uartp->config->cr3 & STM32_UART45_CR3_CHECK_MASK) == 0, + "specified invalid bits in UART5 CR3 register settings"); + + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_UART5_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_UART5_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUART5(FALSE); + nvicEnableVector(STM32_UART5_NUMBER, STM32_UART_UART5_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(UART5_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_UART5_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_USART6 + if (&UARTD6 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART6_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART6_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART6(FALSE); + nvicEnableVector(STM32_USART6_NUMBER, STM32_UART_USART6_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART6_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART6_DMA_PRIORITY); + } +#endif + + /* Static DMA setup, the transfer size depends on the USART settings, + it is 16 bits if M=1 and PCE=0 else it is 8 bits.*/ + if ((uartp->config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_M) + uartp->dmamode |= STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + dmaStreamSetPeripheral(uartp->dmarx, &uartp->usart->DR); + dmaStreamSetPeripheral(uartp->dmatx, &uartp->usart->DR); + uartp->rxbuf = 0; + } + + uartp->rxstate = UART_RX_IDLE; + uartp->txstate = UART_TX_IDLE; + usart_start(uartp); +} + +/** + * @brief Deactivates the UART peripheral. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +void uart_lld_stop(UARTDriver *uartp) { + + if (uartp->state == UART_READY) { + usart_stop(uartp); + dmaStreamRelease(uartp->dmarx); + dmaStreamRelease(uartp->dmatx); + +#if STM32_UART_USE_USART1 + if (&UARTD1 == uartp) { + nvicDisableVector(STM32_USART1_NUMBER); + rccDisableUSART1(FALSE); + return; + } +#endif + +#if STM32_UART_USE_USART2 + if (&UARTD2 == uartp) { + nvicDisableVector(STM32_USART2_NUMBER); + rccDisableUSART2(FALSE); + return; + } +#endif + +#if STM32_UART_USE_USART3 + if (&UARTD3 == uartp) { + nvicDisableVector(STM32_USART3_NUMBER); + rccDisableUSART3(FALSE); + return; + } +#endif + +#if STM32_UART_USE_UART4 + if (&UARTD4 == uartp) { + nvicDisableVector(STM32_UART4_NUMBER); + rccDisableUART4(FALSE); + return; + } +#endif + +#if STM32_UART_USE_UART5 + if (&UARTD5 == uartp) { + nvicDisableVector(STM32_UART5_NUMBER); + rccDisableUART5(FALSE); + return; + } +#endif + +#if STM32_UART_USE_USART6 + if (&UARTD6 == uartp) { + nvicDisableVector(STM32_USART6_NUMBER); + rccDisableUSART6(FALSE); + return; + } +#endif + } +} + +/** + * @brief Starts a transmission on the UART peripheral. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] n number of data frames to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @notapi + */ +void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) { + + /* TX DMA channel preparation.*/ + dmaStreamSetMemory0(uartp->dmatx, txbuf); + dmaStreamSetTransactionSize(uartp->dmatx, n); + dmaStreamSetMode(uartp->dmatx, uartp->dmamode | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + + /* Only enable TC interrupt if there's a callback attached to it. + Also we need to clear TC flag which could be set before. */ + if (uartp->config->txend2_cb != NULL) { + uartp->usart->SR = ~USART_SR_TC; + uartp->usart->CR1 |= USART_CR1_TCIE; + } + + /* Starting transfer.*/ + dmaStreamEnable(uartp->dmatx); +} + +/** + * @brief Stops any ongoing transmission. + * @note Stopping a transmission also suppresses the transmission callbacks. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @return The number of data frames not transmitted by the + * stopped transmit operation. + * + * @notapi + */ +size_t uart_lld_stop_send(UARTDriver *uartp) { + + dmaStreamDisable(uartp->dmatx); + + return dmaStreamGetTransactionSize(uartp->dmatx); +} + +/** + * @brief Starts a receive operation on the UART peripheral. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] n number of data frames to send + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) { + + /* Stopping previous activity (idle state).*/ + dmaStreamDisable(uartp->dmarx); + + /* RX DMA channel preparation.*/ + dmaStreamSetMemory0(uartp->dmarx, rxbuf); + dmaStreamSetTransactionSize(uartp->dmarx, n); + dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + + /* Starting transfer.*/ + dmaStreamEnable(uartp->dmarx); +} + +/** + * @brief Stops any ongoing receive operation. + * @note Stopping a receive operation also suppresses the receive callbacks. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @return The number of data frames not received by the + * stopped receive operation. + * + * @notapi + */ +size_t uart_lld_stop_receive(UARTDriver *uartp) { + size_t n; + + dmaStreamDisable(uartp->dmarx); + n = dmaStreamGetTransactionSize(uartp->dmarx); + uart_enter_rx_idle_loop(uartp); + + return n; +} + +#endif /* HAL_USE_UART */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.h b/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.h new file mode 100644 index 000000000..0eebff3f9 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv1/hal_uart_lld.h @@ -0,0 +1,623 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv1/uart_lld.h + * @brief STM32 low level UART driver header. + * + * @addtogroup UART + * @{ + */ + +#ifndef _UART_LLD_H_ +#define _UART_LLD_H_ + +#if HAL_USE_UART || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief UART driver on USART1 enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART1) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART1 FALSE +#endif + +/** + * @brief UART driver on USART2 enable switch. + * @details If set to @p TRUE the support for USART2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART2) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART2 FALSE +#endif + +/** + * @brief UART driver on USART3 enable switch. + * @details If set to @p TRUE the support for USART3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART3) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART3 FALSE +#endif + +/** + * @brief UART driver on UART4 enable switch. + * @details If set to @p TRUE the support for UART4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_UART4) || defined(__DOXYGEN__) +#define STM32_UART_USE_UART4 FALSE +#endif + +/** + * @brief UART driver on UART5 enable switch. + * @details If set to @p TRUE the support for UART5 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_UART5) || defined(__DOXYGEN__) +#define STM32_UART_USE_UART5 FALSE +#endif + +/** + * @brief UART driver on USART6 enable switch. + * @details If set to @p TRUE the support for USART6 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART6) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART6 FALSE +#endif + +/** + * @brief USART1 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART1_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART2 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART2_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART3 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART3_IRQ_PRIORITY 12 +#endif + +/** + * @brief UART4 interrupt priority level setting. + */ +#if !defined(STM32_UART_UART4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART4_IRQ_PRIORITY 12 +#endif + +/** + * @brief UART5 interrupt priority level setting. + */ +#if !defined(STM32_UART_UART5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART5_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART6 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART6_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART6_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART1 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART1_DMA_PRIORITY 0 +#endif + +/** + * @brief USART2 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART2_DMA_PRIORITY 0 +#endif + +/** + * @brief USART3 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART3_DMA_PRIORITY 0 +#endif + +/** + * @brief UART4 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_UART4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART4_DMA_PRIORITY 0 +#endif + +/** + * @brief UART5 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_UART5_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART5_DMA_PRIORITY 0 +#endif + +/** + * @brief USART6 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART6_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART6_DMA_PRIORITY 0 +#endif + +/** + * @brief USART DMA error hook. + * @note The default action for DMA errors is a system halt because DMA + * error can only happen because programming errors. + */ +#if !defined(STM32_UART_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_UART_USE_USART1 && !STM32_HAS_USART1 +#error "USART1 not present in the selected device" +#endif + +#if STM32_UART_USE_USART2 && !STM32_HAS_USART2 +#error "USART2 not present in the selected device" +#endif + +#if STM32_UART_USE_USART3 && !STM32_HAS_USART3 +#error "USART3 not present in the selected device" +#endif + +#if STM32_UART_USE_UART4 +#if !STM32_HAS_UART4 +#error "UART4 not present in the selected device" +#endif + +#if !defined(STM32F2XX) && !defined(STM32F4XX) +#error "UART4 DMA access not supported in this platform" +#endif +#endif /* STM32_UART_USE_UART4 */ + +#if STM32_UART_USE_UART5 +#if !STM32_HAS_UART5 +#error "UART5 not present in the selected device" +#endif + +#if !defined(STM32F2XX) && !defined(STM32F4XX) +#error "UART5 DMA access not supported in this platform" +#endif +#endif /* STM32_UART_USE_UART5 */ + +#if STM32_UART_USE_USART6 && !STM32_HAS_USART6 +#error "USART6 not present in the selected device" +#endif + +#if !STM32_UART_USE_USART1 && !STM32_UART_USE_USART2 && \ + !STM32_UART_USE_USART3 && !STM32_UART_USE_UART4 && \ + !STM32_UART_USE_UART5 && !STM32_UART_USE_USART6 +#error "UART driver activated but no USART/UART peripheral assigned" +#endif + +#if STM32_UART_USE_USART1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART1" +#endif + +#if STM32_UART_USE_USART2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART2" +#endif + +#if STM32_UART_USE_USART3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART3" +#endif + +#if STM32_UART_USE_UART4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to UART4" +#endif + +#if STM32_UART_USE_UART5 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to UART5" +#endif + +#if STM32_UART_USE_USART6 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART6_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART6" +#endif + +#if STM32_UART_USE_USART1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART1" +#endif + +#if STM32_UART_USE_USART2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART2" +#endif + +#if STM32_UART_USE_USART3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART3" +#endif + +#if STM32_UART_USE_UART4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to UART4" +#endif + +#if STM32_UART_USE_UART5 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART5_DMA_PRIORITY) +#error "Invalid DMA priority assigned to UART5" +#endif + +#if STM32_UART_USE_USART6 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART6_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART6" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_UART_USE_USART1 && (!defined(STM32_UART_USART1_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART1_TX_DMA_STREAM)) +#error "USART1 DMA streams not defined" +#endif + +#if STM32_UART_USE_USART2 && (!defined(STM32_UART_USART2_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART2_TX_DMA_STREAM)) +#error "USART2 DMA streams not defined" +#endif + +#if STM32_UART_USE_USART3 && (!defined(STM32_UART_USART3_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART3_TX_DMA_STREAM)) +#error "USART3 DMA streams not defined" +#endif + +#if STM32_UART_USE_UART4 && (!defined(STM32_UART_UART4_RX_DMA_STREAM) || \ + !defined(STM32_UART_UART4_TX_DMA_STREAM)) +#error "UART4 DMA streams not defined" +#endif + +#if STM32_UART_USE_UART5 && (!defined(STM32_UART_UART5_RX_DMA_STREAM) || \ + !defined(STM32_UART_UART5_TX_DMA_STREAM)) +#error "UART5 DMA streams not defined" +#endif + +#if STM32_UART_USE_USART6 && (!defined(STM32_UART_USART6_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART6_TX_DMA_STREAM)) +#error "USART6 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_UART_USE_USART1 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_RX_DMA_STREAM, \ + STM32_USART1_RX_DMA_MSK) +#error "invalid DMA stream associated to USART1 RX" +#endif + +#if STM32_UART_USE_USART1 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_TX_DMA_STREAM, \ + STM32_USART1_TX_DMA_MSK) +#error "invalid DMA stream associated to USART1 TX" +#endif + +#if STM32_UART_USE_USART2 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_RX_DMA_STREAM, \ + STM32_USART2_RX_DMA_MSK) +#error "invalid DMA stream associated to USART2 RX" +#endif + +#if STM32_UART_USE_USART2 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_TX_DMA_STREAM, \ + STM32_USART2_TX_DMA_MSK) +#error "invalid DMA stream associated to USART2 TX" +#endif + +#if STM32_UART_USE_USART3 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_RX_DMA_STREAM, \ + STM32_USART3_RX_DMA_MSK) +#error "invalid DMA stream associated to USART3 RX" +#endif + +#if STM32_UART_USE_USART3 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_TX_DMA_STREAM, \ + STM32_USART3_TX_DMA_MSK) +#error "invalid DMA stream associated to USART3 TX" +#endif + +#if STM32_UART_USE_UART4 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_RX_DMA_STREAM, \ + STM32_UART4_RX_DMA_MSK) +#error "invalid DMA stream associated to UART4 RX" +#endif + +#if STM32_UART_USE_UART4 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_TX_DMA_STREAM, \ + STM32_UART4_TX_DMA_MSK) +#error "invalid DMA stream associated to UART4 TX" +#endif + +#if STM32_UART_USE_UART5 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_RX_DMA_STREAM, \ + STM32_UART5_RX_DMA_MSK) +#error "invalid DMA stream associated to UART5 RX" +#endif + +#if STM32_UART_USE_UART5 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_TX_DMA_STREAM, \ + STM32_UART5_TX_DMA_MSK) +#error "invalid DMA stream associated to UART5 TX" +#endif + +#if STM32_UART_USE_USART6 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_RX_DMA_STREAM, \ + STM32_USART6_RX_DMA_MSK) +#error "invalid DMA stream associated to USART6 RX" +#endif + +#if STM32_UART_USE_USART6 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_TX_DMA_STREAM, \ + STM32_USART6_TX_DMA_MSK) +#error "invalid DMA stream associated to USART6 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief UART driver condition flags type. + */ +typedef uint32_t uartflags_t; + +/** + * @brief Structure representing an UART driver. + */ +typedef struct UARTDriver UARTDriver; + +/** + * @brief Generic UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +typedef void (*uartcb_t)(UARTDriver *uartp); + +/** + * @brief Character received UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] c received character + */ +typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c); + +/** + * @brief Receive error UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] e receive error mask + */ +typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e); + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief End of transmission buffer callback. + */ + uartcb_t txend1_cb; + /** + * @brief Physical end of transmission callback. + */ + uartcb_t txend2_cb; + /** + * @brief Receive buffer filled callback. + */ + uartcb_t rxend_cb; + /** + * @brief Character received while out if the @p UART_RECEIVE state. + */ + uartccb_t rxchar_cb; + /** + * @brief Receive error callback. + */ + uartecb_t rxerr_cb; + /* End of the mandatory fields.*/ + /** + * @brief Bit rate. + */ + uint32_t speed; + /** + * @brief Initialization value for the CR1 register. + */ + uint16_t cr1; + /** + * @brief Initialization value for the CR2 register. + */ + uint16_t cr2; + /** + * @brief Initialization value for the CR3 register. + */ + uint16_t cr3; +} UARTConfig; + +/** + * @brief Structure representing an UART driver. + */ +struct UARTDriver { + /** + * @brief Driver state. + */ + uartstate_t state; + /** + * @brief Transmitter state. + */ + uarttxstate_t txstate; + /** + * @brief Receiver state. + */ + uartrxstate_t rxstate; + /** + * @brief Current configuration data. + */ + const UARTConfig *config; +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Synchronization flag for transmit operations. + */ + bool early; + /** + * @brief Waiting thread on RX. + */ + thread_reference_t threadrx; + /** + * @brief Waiting thread on TX. + */ + thread_reference_t threadtx; +#endif /* UART_USE_WAIT */ +#if (UART_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* UART_USE_MUTUAL_EXCLUSION */ +#if defined(UART_DRIVER_EXT_FIELDS) + UART_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the USART registers block. + */ + USART_TypeDef *usart; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; + /** + * @brief Receive DMA channel. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief Default receive buffer while into @p UART_RX_IDLE state. + */ + volatile uint16_t rxbuf; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_UART_USE_USART1 && !defined(__DOXYGEN__) +extern UARTDriver UARTD1; +#endif + +#if STM32_UART_USE_USART2 && !defined(__DOXYGEN__) +extern UARTDriver UARTD2; +#endif + +#if STM32_UART_USE_USART3 && !defined(__DOXYGEN__) +extern UARTDriver UARTD3; +#endif + +#if STM32_UART_USE_UART4 && !defined(__DOXYGEN__) +extern UARTDriver UARTD4; +#endif + +#if STM32_UART_USE_UART5 && !defined(__DOXYGEN__) +extern UARTDriver UARTD5; +#endif + +#if STM32_UART_USE_USART6 && !defined(__DOXYGEN__) +extern UARTDriver UARTD6; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void uart_lld_init(void); + void uart_lld_start(UARTDriver *uartp); + void uart_lld_stop(UARTDriver *uartp); + void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf); + size_t uart_lld_stop_send(UARTDriver *uartp); + void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf); + size_t uart_lld_stop_receive(UARTDriver *uartp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_UART */ + +#endif /* _UART_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/serial_lld.c b/os/hal/ports/STM32/LLD/USARTv1/serial_lld.c deleted file mode 100644 index c352b054f..000000000 --- a/os/hal/ports/STM32/LLD/USARTv1/serial_lld.c +++ /dev/null @@ -1,631 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv1/serial_lld.c - * @brief STM32 low level serial driver code. - * - * @addtogroup SERIAL - * @{ - */ - -#include "hal.h" - -#if HAL_USE_SERIAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief USART1 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) -SerialDriver SD1; -#endif - -/** @brief USART2 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) -SerialDriver SD2; -#endif - -/** @brief USART3 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) -SerialDriver SD3; -#endif - -/** @brief UART4 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) -SerialDriver SD4; -#endif - -/** @brief UART5 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) -SerialDriver SD5; -#endif - -/** @brief USART6 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) -SerialDriver SD6; -#endif - -/** @brief UART7 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) -SerialDriver SD7; -#endif - -/** @brief UART8 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -SerialDriver SD8; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/** @brief Driver default configuration.*/ -static const SerialConfig default_config = -{ - SERIAL_DEFAULT_BITRATE, - 0, - USART_CR2_STOP1_BITS | USART_CR2_LINEN, - 0 -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief USART initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] config the architecture-dependent serial driver configuration - */ -static void usart_init(SerialDriver *sdp, const SerialConfig *config) { - USART_TypeDef *u = sdp->usart; - - /* Baud rate setting.*/ -#if STM32_HAS_USART6 - if ((sdp->usart == USART1) || (sdp->usart == USART6)) -#else - if (sdp->usart == USART1) -#endif - u->BRR = STM32_PCLK2 / config->speed; - else - u->BRR = STM32_PCLK1 / config->speed; - - /* Note that some bits are enforced.*/ - u->CR2 = config->cr2 | USART_CR2_LBDIE; - u->CR3 = config->cr3 | USART_CR3_EIE; - u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE | - USART_CR1_RXNEIE | USART_CR1_TE | - USART_CR1_RE; - u->SR = 0; - (void)u->SR; /* SR reset step 1.*/ - (void)u->DR; /* SR reset step 2.*/ -} - -/** - * @brief USART de-initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] u pointer to an USART I/O block - */ -static void usart_deinit(USART_TypeDef *u) { - - u->CR1 = 0; - u->CR2 = 0; - u->CR3 = 0; -} - -/** - * @brief Error handling routine. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] sr USART SR register value - */ -static void set_error(SerialDriver *sdp, uint16_t sr) { - eventflags_t sts = 0; - - if (sr & USART_SR_ORE) - sts |= SD_OVERRUN_ERROR; - if (sr & USART_SR_PE) - sts |= SD_PARITY_ERROR; - if (sr & USART_SR_FE) - sts |= SD_FRAMING_ERROR; - if (sr & USART_SR_NE) - sts |= SD_NOISE_ERROR; - chnAddFlagsI(sdp, sts); -} - -/** - * @brief Common IRQ handler. - * - * @param[in] sdp communication channel associated to the USART - */ -static void serve_interrupt(SerialDriver *sdp) { - USART_TypeDef *u = sdp->usart; - uint16_t cr1 = u->CR1; - uint16_t sr = u->SR; - - /* Special case, LIN break detection.*/ - if (sr & USART_SR_LBD) { - osalSysLockFromISR(); - chnAddFlagsI(sdp, SD_BREAK_DETECTED); - u->SR = ~USART_SR_LBD; - osalSysUnlockFromISR(); - } - - /* Data available.*/ - osalSysLockFromISR(); - while (sr & (USART_SR_RXNE | USART_SR_ORE | USART_SR_NE | USART_SR_FE | - USART_SR_PE)) { - uint8_t b; - - /* Error condition detection.*/ - if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) - set_error(sdp, sr); - b = u->DR; - if (sr & USART_SR_RXNE) - sdIncomingDataI(sdp, b); - sr = u->SR; - } - osalSysUnlockFromISR(); - - /* Transmission buffer empty.*/ - if ((cr1 & USART_CR1_TXEIE) && (sr & USART_SR_TXE)) { - msg_t b; - osalSysLockFromISR(); - b = oqGetI(&sdp->oqueue); - if (b < MSG_OK) { - chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); - u->CR1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; - } - else - u->DR = b; - osalSysUnlockFromISR(); - } - - /* Physical transmission end.*/ - if (sr & USART_SR_TC) { - osalSysLockFromISR(); - if (oqIsEmptyI(&sdp->oqueue)) - chnAddFlagsI(sdp, CHN_TRANSMISSION_END); - u->CR1 = cr1 & ~USART_CR1_TCIE; - u->SR = ~USART_SR_TC; - osalSysUnlockFromISR(); - } -} - -#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) -static void notify1(io_queue_t *qp) { - - (void)qp; - USART1->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) -static void notify2(io_queue_t *qp) { - - (void)qp; - USART2->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) -static void notify3(io_queue_t *qp) { - - (void)qp; - USART3->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) -static void notify4(io_queue_t *qp) { - - (void)qp; - UART4->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) -static void notify5(io_queue_t *qp) { - - (void)qp; - UART5->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) -static void notify6(io_queue_t *qp) { - - (void)qp; - USART6->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) -static void notify7(io_queue_t *qp) { - - (void)qp; - UART7->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -static void notify8(io_queue_t *qp) { - - (void)qp; - UART8->CR1 |= USART_CR1_TXEIE; -} -#endif - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) -#if !defined(STM32_USART1_HANDLER) -#error "STM32_USART1_HANDLER not defined" -#endif -/** - * @brief USART1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) -#if !defined(STM32_USART2_HANDLER) -#error "STM32_USART2_HANDLER not defined" -#endif -/** - * @brief USART2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) -#if !defined(STM32_USART3_HANDLER) -#error "STM32_USART3_HANDLER not defined" -#endif -/** - * @brief USART3 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) -#if !defined(STM32_UART4_HANDLER) -#error "STM32_UART4_HANDLER not defined" -#endif -/** - * @brief UART4 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) -#if !defined(STM32_UART5_HANDLER) -#error "STM32_UART5_HANDLER not defined" -#endif -/** - * @brief UART5 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) -#if !defined(STM32_USART6_HANDLER) -#error "STM32_USART6_HANDLER not defined" -#endif -/** - * @brief USART6 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD6); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) -#if !defined(STM32_UART7_HANDLER) -#error "STM32_UART7_HANDLER not defined" -#endif -/** - * @brief UART7 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART7_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD7); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -#if !defined(STM32_UART8_HANDLER) -#error "STM32_UART8_HANDLER not defined" -#endif -/** - * @brief UART8 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART8_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD8); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level serial driver initialization. - * - * @notapi - */ -void sd_lld_init(void) { - -#if STM32_SERIAL_USE_USART1 - sdObjectInit(&SD1, NULL, notify1); - SD1.usart = USART1; -#endif - -#if STM32_SERIAL_USE_USART2 - sdObjectInit(&SD2, NULL, notify2); - SD2.usart = USART2; -#endif - -#if STM32_SERIAL_USE_USART3 - sdObjectInit(&SD3, NULL, notify3); - SD3.usart = USART3; -#endif - -#if STM32_SERIAL_USE_UART4 - sdObjectInit(&SD4, NULL, notify4); - SD4.usart = UART4; -#endif - -#if STM32_SERIAL_USE_UART5 - sdObjectInit(&SD5, NULL, notify5); - SD5.usart = UART5; -#endif - -#if STM32_SERIAL_USE_USART6 - sdObjectInit(&SD6, NULL, notify6); - SD6.usart = USART6; -#endif - -#if STM32_SERIAL_USE_UART7 - sdObjectInit(&SD7, NULL, notify7); - SD7.usart = UART7; -#endif - -#if STM32_SERIAL_USE_UART8 - sdObjectInit(&SD8, NULL, notify8); - SD8.usart = UART8; -#endif -} - -/** - * @brief Low level serial driver configuration and (re)start. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] config the architecture-dependent serial driver configuration. - * If this parameter is set to @p NULL then a default - * configuration is used. - * - * @notapi - */ -void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { - - if (config == NULL) - config = &default_config; - - if (sdp->state == SD_STOP) { -#if STM32_SERIAL_USE_USART1 - if (&SD1 == sdp) { - rccEnableUSART1(FALSE); - nvicEnableVector(STM32_USART1_NUMBER, STM32_SERIAL_USART1_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_USART2 - if (&SD2 == sdp) { - rccEnableUSART2(FALSE); - nvicEnableVector(STM32_USART2_NUMBER, STM32_SERIAL_USART2_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_USART3 - if (&SD3 == sdp) { - rccEnableUSART3(FALSE); - nvicEnableVector(STM32_USART3_NUMBER, STM32_SERIAL_USART3_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_UART4 - if (&SD4 == sdp) { - rccEnableUART4(FALSE); - nvicEnableVector(STM32_UART4_NUMBER, STM32_SERIAL_UART4_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_UART5 - if (&SD5 == sdp) { - rccEnableUART5(FALSE); - nvicEnableVector(STM32_UART5_NUMBER, STM32_SERIAL_UART5_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_USART6 - if (&SD6 == sdp) { - rccEnableUSART6(FALSE); - nvicEnableVector(STM32_USART6_NUMBER, STM32_SERIAL_USART6_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_UART7 - if (&SD7 == sdp) { - rccEnableUART7(FALSE); - nvicEnableVector(STM32_UART7_NUMBER, STM32_SERIAL_UART7_PRIORITY); - } -#endif -#if STM32_SERIAL_USE_UART8 - if (&SD8 == sdp) { - rccEnableUART8(FALSE); - nvicEnableVector(STM32_UART8_NUMBER, STM32_SERIAL_UART8_PRIORITY); - } -#endif - } - usart_init(sdp, config); -} - -/** - * @brief Low level serial driver stop. - * @details De-initializes the USART, stops the associated clock, resets the - * interrupt vector. - * - * @param[in] sdp pointer to a @p SerialDriver object - * - * @notapi - */ -void sd_lld_stop(SerialDriver *sdp) { - - if (sdp->state == SD_READY) { - usart_deinit(sdp->usart); -#if STM32_SERIAL_USE_USART1 - if (&SD1 == sdp) { - rccDisableUSART1(FALSE); - nvicDisableVector(STM32_USART1_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_USART2 - if (&SD2 == sdp) { - rccDisableUSART2(FALSE); - nvicDisableVector(STM32_USART2_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_USART3 - if (&SD3 == sdp) { - rccDisableUSART3(FALSE); - nvicDisableVector(STM32_USART3_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_UART4 - if (&SD4 == sdp) { - rccDisableUART4(FALSE); - nvicDisableVector(STM32_UART4_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_UART5 - if (&SD5 == sdp) { - rccDisableUART5(FALSE); - nvicDisableVector(STM32_UART5_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_USART6 - if (&SD6 == sdp) { - rccDisableUSART6(FALSE); - nvicDisableVector(STM32_USART6_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_UART7 - if (&SD7 == sdp) { - rccDisableUART7(FALSE); - nvicDisableVector(STM32_UART7_NUMBER); - return; - } -#endif -#if STM32_SERIAL_USE_UART8 - if (&SD8 == sdp) { - rccDisableUART8(FALSE); - nvicDisableVector(STM32_UART8_NUMBER); - return; - } -#endif - } -} - -#endif /* HAL_USE_SERIAL */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/serial_lld.h b/os/hal/ports/STM32/LLD/USARTv1/serial_lld.h deleted file mode 100644 index 0e76089b5..000000000 --- a/os/hal/ports/STM32/LLD/USARTv1/serial_lld.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv1/serial_lld.h - * @brief STM32 low level serial driver header. - * - * @addtogroup SERIAL - * @{ - */ - -#ifndef _SERIAL_LLD_H_ -#define _SERIAL_LLD_H_ - -#if HAL_USE_SERIAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief USART1 driver enable switch. - * @details If set to @p TRUE the support for USART1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART1) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART1 FALSE -#endif - -/** - * @brief USART2 driver enable switch. - * @details If set to @p TRUE the support for USART2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART2) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART2 FALSE -#endif - -/** - * @brief USART3 driver enable switch. - * @details If set to @p TRUE the support for USART3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART3) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART3 FALSE -#endif - -/** - * @brief UART4 driver enable switch. - * @details If set to @p TRUE the support for UART4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART4) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART4 FALSE -#endif - -/** - * @brief UART5 driver enable switch. - * @details If set to @p TRUE the support for UART5 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART5) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART5 FALSE -#endif - -/** - * @brief USART6 driver enable switch. - * @details If set to @p TRUE the support for USART6 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART6) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART6 FALSE -#endif - -/** - * @brief UART7 driver enable switch. - * @details If set to @p TRUE the support for UART7 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART7) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART7 FALSE -#endif - -/** - * @brief UART8 driver enable switch. - * @details If set to @p TRUE the support for UART8 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART8) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART8 FALSE -#endif - -/** - * @brief USART1 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART1_PRIORITY 12 -#endif - -/** - * @brief USART2 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART2_PRIORITY 12 -#endif - -/** - * @brief USART3 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART3_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART3_PRIORITY 12 -#endif - -/** - * @brief UART4 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART4_PRIORITY 12 -#endif - -/** - * @brief UART5 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART5_PRIORITY 12 -#endif - -/** - * @brief USART6 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART6_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART6_PRIORITY 12 -#endif - -/** - * @brief UART7 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART7_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART7_PRIORITY 12 -#endif - -/** - * @brief UART8 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART8_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART8_PRIORITY 12 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_SERIAL_USE_USART1 && !STM32_HAS_USART1 -#error "USART1 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_USART2 && !STM32_HAS_USART2 -#error "USART2 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_USART3 && !STM32_HAS_USART3 -#error "USART3 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART4 && !STM32_HAS_UART4 -#error "UART4 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART5 && !STM32_HAS_UART5 -#error "UART5 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_USART6 && !STM32_HAS_USART6 -#error "USART6 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART7 && !STM32_HAS_UART7 -#error "UART7 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART8 && !STM32_HAS_UART8 -#error "UART8 not present in the selected device" -#endif - -#if !STM32_SERIAL_USE_USART1 && !STM32_SERIAL_USE_USART2 && \ - !STM32_SERIAL_USE_USART3 && !STM32_SERIAL_USE_UART4 && \ - !STM32_SERIAL_USE_UART5 && !STM32_SERIAL_USE_USART6 && \ - !STM32_SERIAL_USE_UART7 && !STM32_SERIAL_USE_UART8 -#error "SERIAL driver activated but no USART/UART peripheral assigned" -#endif - -#if STM32_SERIAL_USE_USART1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART1_PRIORITY) -#error "Invalid IRQ priority assigned to USART1" -#endif - -#if STM32_SERIAL_USE_USART2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART2_PRIORITY) -#error "Invalid IRQ priority assigned to USART2" -#endif - -#if STM32_SERIAL_USE_USART3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART3_PRIORITY) -#error "Invalid IRQ priority assigned to USART3" -#endif - -#if STM32_SERIAL_USE_UART4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART4_PRIORITY) -#error "Invalid IRQ priority assigned to UART4" -#endif - -#if STM32_SERIAL_USE_UART5 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART5_PRIORITY) -#error "Invalid IRQ priority assigned to UART5" -#endif - -#if STM32_SERIAL_USE_USART6 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART6_PRIORITY) -#error "Invalid IRQ priority assigned to USART6" -#endif - -#if STM32_SERIAL_USE_UART7 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART7_PRIORITY) -#error "Invalid IRQ priority assigned to UART7" -#endif - -#if STM32_SERIAL_USE_UART8 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART8_PRIORITY) -#error "Invalid IRQ priority assigned to UART8" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief STM32 Serial Driver configuration structure. - * @details An instance of this structure must be passed to @p sdStart() - * in order to configure and start a serial driver operations. - * @note This structure content is architecture dependent, each driver - * implementation defines its own version and the custom static - * initializers. - */ -typedef struct { - /** - * @brief Bit rate. - */ - uint32_t speed; - /* End of the mandatory fields.*/ - /** - * @brief Initialization value for the CR1 register. - */ - uint16_t cr1; - /** - * @brief Initialization value for the CR2 register. - */ - uint16_t cr2; - /** - * @brief Initialization value for the CR3 register. - */ - uint16_t cr3; -} SerialConfig; - -/** - * @brief @p SerialDriver specific data. - */ -#define _serial_driver_data \ - _base_asynchronous_channel_data \ - /* Driver state.*/ \ - sdstate_t state; \ - /* Input queue.*/ \ - input_queue_t iqueue; \ - /* Output queue.*/ \ - output_queue_t oqueue; \ - /* Input circular buffer.*/ \ - uint8_t ib[SERIAL_BUFFERS_SIZE]; \ - /* Output circular buffer.*/ \ - uint8_t ob[SERIAL_BUFFERS_SIZE]; \ - /* End of the mandatory fields.*/ \ - /* Pointer to the USART registers block.*/ \ - USART_TypeDef *usart; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/* - * Extra USARTs definitions here (missing from the ST header file). - */ -#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/ -#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/ -#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/ -#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_SERIAL_USE_USART1 && !defined(__DOXYGEN__) -extern SerialDriver SD1; -#endif -#if STM32_SERIAL_USE_USART2 && !defined(__DOXYGEN__) -extern SerialDriver SD2; -#endif -#if STM32_SERIAL_USE_USART3 && !defined(__DOXYGEN__) -extern SerialDriver SD3; -#endif -#if STM32_SERIAL_USE_UART4 && !defined(__DOXYGEN__) -extern SerialDriver SD4; -#endif -#if STM32_SERIAL_USE_UART5 && !defined(__DOXYGEN__) -extern SerialDriver SD5; -#endif -#if STM32_SERIAL_USE_USART6 && !defined(__DOXYGEN__) -extern SerialDriver SD6; -#endif -#if STM32_SERIAL_USE_UART7 && !defined(__DOXYGEN__) -extern SerialDriver SD7; -#endif -#if STM32_SERIAL_USE_UART8 && !defined(__DOXYGEN__) -extern SerialDriver SD8; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void sd_lld_init(void); - void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); - void sd_lld_stop(SerialDriver *sdp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SERIAL */ - -#endif /* _SERIAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c deleted file mode 100644 index 9019d83e6..000000000 --- a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.c +++ /dev/null @@ -1,811 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv1/uart_lld.c - * @brief STM32 low level UART driver code. - * - * @addtogroup UART - * @{ - */ - -#include "hal.h" - -#if HAL_USE_UART || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define USART1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART1_RX_DMA_STREAM, \ - STM32_USART1_RX_DMA_CHN) - -#define USART1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART1_TX_DMA_STREAM, \ - STM32_USART1_TX_DMA_CHN) - -#define USART2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART2_RX_DMA_STREAM, \ - STM32_USART2_RX_DMA_CHN) - -#define USART2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART2_TX_DMA_STREAM, \ - STM32_USART2_TX_DMA_CHN) - -#define USART3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART3_RX_DMA_STREAM, \ - STM32_USART3_RX_DMA_CHN) - -#define USART3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART3_TX_DMA_STREAM, \ - STM32_USART3_TX_DMA_CHN) - -#define UART4_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART4_RX_DMA_STREAM, \ - STM32_UART4_RX_DMA_CHN) - -#define UART4_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART4_TX_DMA_STREAM, \ - STM32_UART4_TX_DMA_CHN) - -#define UART5_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART5_RX_DMA_STREAM, \ - STM32_UART5_RX_DMA_CHN) - -#define UART5_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART5_TX_DMA_STREAM, \ - STM32_UART5_TX_DMA_CHN) - -#define USART6_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART6_RX_DMA_STREAM, \ - STM32_USART6_RX_DMA_CHN) - -#define USART6_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART6_TX_DMA_STREAM, \ - STM32_USART6_TX_DMA_CHN) - -#define STM32_UART45_CR2_CHECK_MASK \ - (USART_CR2_STOP_0 | USART_CR2_CLKEN | USART_CR2_CPOL | USART_CR2_CPHA | \ - USART_CR2_LBCL) - -#define STM32_UART45_CR3_CHECK_MASK \ - (USART_CR3_CTSIE | USART_CR3_CTSE | USART_CR3_RTSE | USART_CR3_SCEN | \ - USART_CR3_NACK) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief USART1 UART driver identifier.*/ -#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) -UARTDriver UARTD1; -#endif - -/** @brief USART2 UART driver identifier.*/ -#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) -UARTDriver UARTD2; -#endif - -/** @brief USART3 UART driver identifier.*/ -#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) -UARTDriver UARTD3; -#endif - -/** @brief UART4 UART driver identifier.*/ -#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) -UARTDriver UARTD4; -#endif - -/** @brief UART5 UART driver identifier.*/ -#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) -UARTDriver UARTD5; -#endif - -/** @brief USART6 UART driver identifier.*/ -#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) -UARTDriver UARTD6; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Status bits translation. - * - * @param[in] sr USART SR register value - * - * @return The error flags. - */ -static uartflags_t translate_errors(uint16_t sr) { - uartflags_t sts = 0; - - if (sr & USART_SR_ORE) - sts |= UART_OVERRUN_ERROR; - if (sr & USART_SR_PE) - sts |= UART_PARITY_ERROR; - if (sr & USART_SR_FE) - sts |= UART_FRAMING_ERROR; - if (sr & USART_SR_NE) - sts |= UART_NOISE_ERROR; - if (sr & USART_SR_LBD) - sts |= UART_BREAK_DETECTED; - return sts; -} - -/** - * @brief Puts the receiver in the UART_RX_IDLE state. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void uart_enter_rx_idle_loop(UARTDriver *uartp) { - uint32_t mode; - - /* RX DMA channel preparation, if the char callback is defined then the - TCIE interrupt is enabled too.*/ - if (uartp->config->rxchar_cb == NULL) - mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC; - else - mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE; - dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf); - dmaStreamSetTransactionSize(uartp->dmarx, 1); - dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode); - dmaStreamEnable(uartp->dmarx); -} - -/** - * @brief USART de-initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void usart_stop(UARTDriver *uartp) { - - /* Stops RX and TX DMA channels.*/ - dmaStreamDisable(uartp->dmarx); - dmaStreamDisable(uartp->dmatx); - - /* Stops USART operations.*/ - uartp->usart->CR1 = 0; - uartp->usart->CR2 = 0; - uartp->usart->CR3 = 0; -} - -/** - * @brief USART initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void usart_start(UARTDriver *uartp) { - uint16_t cr1; - USART_TypeDef *u = uartp->usart; - - /* Defensive programming, starting from a clean state.*/ - usart_stop(uartp); - - /* Baud rate setting.*/ -#if STM32_HAS_USART6 - if ((uartp->usart == USART1) || (uartp->usart == USART6)) -#else - if (uartp->usart == USART1) -#endif - u->BRR = STM32_PCLK2 / uartp->config->speed; - else - u->BRR = STM32_PCLK1 / uartp->config->speed; - - /* Resetting eventual pending status flags.*/ - (void)u->SR; /* SR reset step 1.*/ - (void)u->DR; /* SR reset step 2.*/ - u->SR = 0; - - /* Note that some bits are enforced because required for correct driver - operations.*/ - u->CR2 = uartp->config->cr2 | USART_CR2_LBDIE; - u->CR3 = uartp->config->cr3 | USART_CR3_DMAT | USART_CR3_DMAR | - USART_CR3_EIE; - - /* Mustn't ever set TCIE here - if done, it causes an immediate - interrupt.*/ - cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE; - u->CR1 = uartp->config->cr1 | cr1; - - /* Starting the receiver idle loop.*/ - uart_enter_rx_idle_loop(uartp); -} - -/** - * @brief RX DMA common service routine. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_UART_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_UART_DMA_ERROR_HOOK(uartp); - } -#else - (void)flags; -#endif - - if (uartp->rxstate == UART_RX_IDLE) { - /* Receiver in idle state, a callback is generated, if enabled, for each - received character and then the driver stays in the same state.*/ - _uart_rx_idle_code(uartp); - } - else { - /* Receiver in active state, a callback is generated, if enabled, after - a completed transfer.*/ - dmaStreamDisable(uartp->dmarx); - _uart_rx_complete_isr_code(uartp); - } -} - -/** - * @brief TX DMA common service routine. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_UART_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_UART_DMA_ERROR_HOOK(uartp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(uartp->dmatx); - - /* A callback is generated, if enabled, after a completed transfer.*/ - _uart_tx1_isr_code(uartp); -} - -/** - * @brief USART common service routine. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void serve_usart_irq(UARTDriver *uartp) { - uint16_t sr; - USART_TypeDef *u = uartp->usart; - uint32_t cr1 = u->CR1; - - sr = u->SR; /* SR reset step 1.*/ - (void)u->DR; /* SR reset step 2.*/ - - if (sr & (USART_SR_LBD | USART_SR_ORE | USART_SR_NE | - USART_SR_FE | USART_SR_PE)) { - u->SR = ~USART_SR_LBD; - _uart_rx_error_isr_code(uartp, translate_errors(sr)); - } - - if ((sr & USART_SR_TC) && (cr1 & USART_CR1_TCIE)) { - /* TC interrupt cleared and disabled.*/ - u->SR = ~USART_SR_TC; - u->CR1 = cr1 & ~USART_CR1_TCIE; - - /* End of transmission, a callback is generated.*/ - _uart_tx2_isr_code(uartp); - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) -#if !defined(STM32_USART1_HANDLER) -#error "STM32_USART1_HANDLER not defined" -#endif -/** - * @brief USART1 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART1 */ - -#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) -#if !defined(STM32_USART2_HANDLER) -#error "STM32_USART2_HANDLER not defined" -#endif -/** - * @brief USART2 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART2 */ - -#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) -#if !defined(STM32_USART3_HANDLER) -#error "STM32_USART3_HANDLER not defined" -#endif -/** - * @brief USART3 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART3 */ - -#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) -#if !defined(STM32_UART4_HANDLER) -#error "STM32_UART4_HANDLER not defined" -#endif -/** - * @brief UART4 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_UART4 */ - -#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) -#if !defined(STM32_UART5_HANDLER) -#error "STM32_UART5_HANDLER not defined" -#endif -/** - * @brief UART5 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_UART5 */ - -#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) -#if !defined(STM32_USART6_HANDLER) -#error "STM32_USART6_HANDLER not defined" -#endif -/** - * @brief USART6 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD6); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART6 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level UART driver initialization. - * - * @notapi - */ -void uart_lld_init(void) { - -#if STM32_UART_USE_USART1 - uartObjectInit(&UARTD1); - UARTD1.usart = USART1; - UARTD1.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD1.dmarx = STM32_DMA_STREAM(STM32_UART_USART1_RX_DMA_STREAM); - UARTD1.dmatx = STM32_DMA_STREAM(STM32_UART_USART1_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_USART2 - uartObjectInit(&UARTD2); - UARTD2.usart = USART2; - UARTD2.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD2.dmarx = STM32_DMA_STREAM(STM32_UART_USART2_RX_DMA_STREAM); - UARTD2.dmatx = STM32_DMA_STREAM(STM32_UART_USART2_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_USART3 - uartObjectInit(&UARTD3); - UARTD3.usart = USART3; - UARTD3.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD3.dmarx = STM32_DMA_STREAM(STM32_UART_USART3_RX_DMA_STREAM); - UARTD3.dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_UART4 - uartObjectInit(&UARTD4); - UARTD4.usart = UART4; - UARTD4.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD4.dmarx = STM32_DMA_STREAM(STM32_UART_UART4_RX_DMA_STREAM); - UARTD4.dmatx = STM32_DMA_STREAM(STM32_UART_UART4_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_UART5 - uartObjectInit(&UARTD5); - UARTD5.usart = UART5; - UARTD5.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD5.dmarx = STM32_DMA_STREAM(STM32_UART_UART5_RX_DMA_STREAM); - UARTD5.dmatx = STM32_DMA_STREAM(STM32_UART_UART5_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_USART6 - uartObjectInit(&UARTD6); - UARTD6.usart = USART6; - UARTD6.dmarx = STM32_DMA_STREAM(STM32_UART_USART6_RX_DMA_STREAM); - UARTD6.dmatx = STM32_DMA_STREAM(STM32_UART_USART6_TX_DMA_STREAM); -#endif -} - -/** - * @brief Configures and activates the UART peripheral. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @notapi - */ -void uart_lld_start(UARTDriver *uartp) { - - if (uartp->state == UART_STOP) { -#if STM32_UART_USE_USART1 - if (&UARTD1 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART1_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART1_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART1(FALSE); - nvicEnableVector(STM32_USART1_NUMBER, STM32_UART_USART1_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART1_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_USART2 - if (&UARTD2 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART2_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART2_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART2(FALSE); - nvicEnableVector(STM32_USART2_NUMBER, STM32_UART_USART2_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART2_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_USART3 - if (&UARTD3 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART3_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART3_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART3(FALSE); - nvicEnableVector(STM32_USART3_NUMBER, STM32_UART_USART3_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_UART4 - if (&UARTD4 == uartp) { - bool b; - - chDbgAssert((uartp->config->cr2 & STM32_UART45_CR2_CHECK_MASK) == 0, - "specified invalid bits in UART4 CR2 register settings"); - chDbgAssert((uartp->config->cr3 & STM32_UART45_CR3_CHECK_MASK) == 0, - "specified invalid bits in UART4 CR3 register settings"); - - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_UART4_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_UART4_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUART4(FALSE); - nvicEnableVector(STM32_UART4_NUMBER, STM32_UART_UART4_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(UART4_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_UART4_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_UART5 - if (&UARTD5 == uartp) { - bool b; - - chDbgAssert((uartp->config->cr2 & STM32_UART45_CR2_CHECK_MASK) == 0, - "specified invalid bits in UART5 CR2 register settings"); - chDbgAssert((uartp->config->cr3 & STM32_UART45_CR3_CHECK_MASK) == 0, - "specified invalid bits in UART5 CR3 register settings"); - - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_UART5_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_UART5_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUART5(FALSE); - nvicEnableVector(STM32_UART5_NUMBER, STM32_UART_UART5_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(UART5_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_UART5_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_USART6 - if (&UARTD6 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART6_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART6_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART6(FALSE); - nvicEnableVector(STM32_USART6_NUMBER, STM32_UART_USART6_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART6_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART6_DMA_PRIORITY); - } -#endif - - /* Static DMA setup, the transfer size depends on the USART settings, - it is 16 bits if M=1 and PCE=0 else it is 8 bits.*/ - if ((uartp->config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_M) - uartp->dmamode |= STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - dmaStreamSetPeripheral(uartp->dmarx, &uartp->usart->DR); - dmaStreamSetPeripheral(uartp->dmatx, &uartp->usart->DR); - uartp->rxbuf = 0; - } - - uartp->rxstate = UART_RX_IDLE; - uartp->txstate = UART_TX_IDLE; - usart_start(uartp); -} - -/** - * @brief Deactivates the UART peripheral. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @notapi - */ -void uart_lld_stop(UARTDriver *uartp) { - - if (uartp->state == UART_READY) { - usart_stop(uartp); - dmaStreamRelease(uartp->dmarx); - dmaStreamRelease(uartp->dmatx); - -#if STM32_UART_USE_USART1 - if (&UARTD1 == uartp) { - nvicDisableVector(STM32_USART1_NUMBER); - rccDisableUSART1(FALSE); - return; - } -#endif - -#if STM32_UART_USE_USART2 - if (&UARTD2 == uartp) { - nvicDisableVector(STM32_USART2_NUMBER); - rccDisableUSART2(FALSE); - return; - } -#endif - -#if STM32_UART_USE_USART3 - if (&UARTD3 == uartp) { - nvicDisableVector(STM32_USART3_NUMBER); - rccDisableUSART3(FALSE); - return; - } -#endif - -#if STM32_UART_USE_UART4 - if (&UARTD4 == uartp) { - nvicDisableVector(STM32_UART4_NUMBER); - rccDisableUART4(FALSE); - return; - } -#endif - -#if STM32_UART_USE_UART5 - if (&UARTD5 == uartp) { - nvicDisableVector(STM32_UART5_NUMBER); - rccDisableUART5(FALSE); - return; - } -#endif - -#if STM32_UART_USE_USART6 - if (&UARTD6 == uartp) { - nvicDisableVector(STM32_USART6_NUMBER); - rccDisableUSART6(FALSE); - return; - } -#endif - } -} - -/** - * @brief Starts a transmission on the UART peripheral. - * @note The buffers are organized as uint8_t arrays for data sizes below - * or equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] n number of data frames to send - * @param[in] txbuf the pointer to the transmit buffer - * - * @notapi - */ -void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) { - - /* TX DMA channel preparation.*/ - dmaStreamSetMemory0(uartp->dmatx, txbuf); - dmaStreamSetTransactionSize(uartp->dmatx, n); - dmaStreamSetMode(uartp->dmatx, uartp->dmamode | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); - - /* Only enable TC interrupt if there's a callback attached to it. - Also we need to clear TC flag which could be set before. */ - if (uartp->config->txend2_cb != NULL) { - uartp->usart->SR = ~USART_SR_TC; - uartp->usart->CR1 |= USART_CR1_TCIE; - } - - /* Starting transfer.*/ - dmaStreamEnable(uartp->dmatx); -} - -/** - * @brief Stops any ongoing transmission. - * @note Stopping a transmission also suppresses the transmission callbacks. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @return The number of data frames not transmitted by the - * stopped transmit operation. - * - * @notapi - */ -size_t uart_lld_stop_send(UARTDriver *uartp) { - - dmaStreamDisable(uartp->dmatx); - - return dmaStreamGetTransactionSize(uartp->dmatx); -} - -/** - * @brief Starts a receive operation on the UART peripheral. - * @note The buffers are organized as uint8_t arrays for data sizes below - * or equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] n number of data frames to send - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) { - - /* Stopping previous activity (idle state).*/ - dmaStreamDisable(uartp->dmarx); - - /* RX DMA channel preparation.*/ - dmaStreamSetMemory0(uartp->dmarx, rxbuf); - dmaStreamSetTransactionSize(uartp->dmarx, n); - dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); - - /* Starting transfer.*/ - dmaStreamEnable(uartp->dmarx); -} - -/** - * @brief Stops any ongoing receive operation. - * @note Stopping a receive operation also suppresses the receive callbacks. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @return The number of data frames not received by the - * stopped receive operation. - * - * @notapi - */ -size_t uart_lld_stop_receive(UARTDriver *uartp) { - size_t n; - - dmaStreamDisable(uartp->dmarx); - n = dmaStreamGetTransactionSize(uartp->dmarx); - uart_enter_rx_idle_loop(uartp); - - return n; -} - -#endif /* HAL_USE_UART */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h b/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h deleted file mode 100644 index 0eebff3f9..000000000 --- a/os/hal/ports/STM32/LLD/USARTv1/uart_lld.h +++ /dev/null @@ -1,623 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv1/uart_lld.h - * @brief STM32 low level UART driver header. - * - * @addtogroup UART - * @{ - */ - -#ifndef _UART_LLD_H_ -#define _UART_LLD_H_ - -#if HAL_USE_UART || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief UART driver on USART1 enable switch. - * @details If set to @p TRUE the support for USART1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART1) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART1 FALSE -#endif - -/** - * @brief UART driver on USART2 enable switch. - * @details If set to @p TRUE the support for USART2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART2) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART2 FALSE -#endif - -/** - * @brief UART driver on USART3 enable switch. - * @details If set to @p TRUE the support for USART3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART3) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART3 FALSE -#endif - -/** - * @brief UART driver on UART4 enable switch. - * @details If set to @p TRUE the support for UART4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_UART4) || defined(__DOXYGEN__) -#define STM32_UART_USE_UART4 FALSE -#endif - -/** - * @brief UART driver on UART5 enable switch. - * @details If set to @p TRUE the support for UART5 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_UART5) || defined(__DOXYGEN__) -#define STM32_UART_USE_UART5 FALSE -#endif - -/** - * @brief UART driver on USART6 enable switch. - * @details If set to @p TRUE the support for USART6 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART6) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART6 FALSE -#endif - -/** - * @brief USART1 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART1_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART2 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART2_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART3 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART3_IRQ_PRIORITY 12 -#endif - -/** - * @brief UART4 interrupt priority level setting. - */ -#if !defined(STM32_UART_UART4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART4_IRQ_PRIORITY 12 -#endif - -/** - * @brief UART5 interrupt priority level setting. - */ -#if !defined(STM32_UART_UART5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART5_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART6 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART6_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART6_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART1 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART1_DMA_PRIORITY 0 -#endif - -/** - * @brief USART2 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART2_DMA_PRIORITY 0 -#endif - -/** - * @brief USART3 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART3_DMA_PRIORITY 0 -#endif - -/** - * @brief UART4 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_UART4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART4_DMA_PRIORITY 0 -#endif - -/** - * @brief UART5 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_UART5_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART5_DMA_PRIORITY 0 -#endif - -/** - * @brief USART6 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART6_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART6_DMA_PRIORITY 0 -#endif - -/** - * @brief USART DMA error hook. - * @note The default action for DMA errors is a system halt because DMA - * error can only happen because programming errors. - */ -#if !defined(STM32_UART_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_UART_USE_USART1 && !STM32_HAS_USART1 -#error "USART1 not present in the selected device" -#endif - -#if STM32_UART_USE_USART2 && !STM32_HAS_USART2 -#error "USART2 not present in the selected device" -#endif - -#if STM32_UART_USE_USART3 && !STM32_HAS_USART3 -#error "USART3 not present in the selected device" -#endif - -#if STM32_UART_USE_UART4 -#if !STM32_HAS_UART4 -#error "UART4 not present in the selected device" -#endif - -#if !defined(STM32F2XX) && !defined(STM32F4XX) -#error "UART4 DMA access not supported in this platform" -#endif -#endif /* STM32_UART_USE_UART4 */ - -#if STM32_UART_USE_UART5 -#if !STM32_HAS_UART5 -#error "UART5 not present in the selected device" -#endif - -#if !defined(STM32F2XX) && !defined(STM32F4XX) -#error "UART5 DMA access not supported in this platform" -#endif -#endif /* STM32_UART_USE_UART5 */ - -#if STM32_UART_USE_USART6 && !STM32_HAS_USART6 -#error "USART6 not present in the selected device" -#endif - -#if !STM32_UART_USE_USART1 && !STM32_UART_USE_USART2 && \ - !STM32_UART_USE_USART3 && !STM32_UART_USE_UART4 && \ - !STM32_UART_USE_UART5 && !STM32_UART_USE_USART6 -#error "UART driver activated but no USART/UART peripheral assigned" -#endif - -#if STM32_UART_USE_USART1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART1" -#endif - -#if STM32_UART_USE_USART2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART2" -#endif - -#if STM32_UART_USE_USART3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART3" -#endif - -#if STM32_UART_USE_UART4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to UART4" -#endif - -#if STM32_UART_USE_UART5 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to UART5" -#endif - -#if STM32_UART_USE_USART6 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART6_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART6" -#endif - -#if STM32_UART_USE_USART1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART1" -#endif - -#if STM32_UART_USE_USART2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART2" -#endif - -#if STM32_UART_USE_USART3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART3" -#endif - -#if STM32_UART_USE_UART4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to UART4" -#endif - -#if STM32_UART_USE_UART5 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART5_DMA_PRIORITY) -#error "Invalid DMA priority assigned to UART5" -#endif - -#if STM32_UART_USE_USART6 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART6_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART6" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_UART_USE_USART1 && (!defined(STM32_UART_USART1_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART1_TX_DMA_STREAM)) -#error "USART1 DMA streams not defined" -#endif - -#if STM32_UART_USE_USART2 && (!defined(STM32_UART_USART2_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART2_TX_DMA_STREAM)) -#error "USART2 DMA streams not defined" -#endif - -#if STM32_UART_USE_USART3 && (!defined(STM32_UART_USART3_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART3_TX_DMA_STREAM)) -#error "USART3 DMA streams not defined" -#endif - -#if STM32_UART_USE_UART4 && (!defined(STM32_UART_UART4_RX_DMA_STREAM) || \ - !defined(STM32_UART_UART4_TX_DMA_STREAM)) -#error "UART4 DMA streams not defined" -#endif - -#if STM32_UART_USE_UART5 && (!defined(STM32_UART_UART5_RX_DMA_STREAM) || \ - !defined(STM32_UART_UART5_TX_DMA_STREAM)) -#error "UART5 DMA streams not defined" -#endif - -#if STM32_UART_USE_USART6 && (!defined(STM32_UART_USART6_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART6_TX_DMA_STREAM)) -#error "USART6 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_UART_USE_USART1 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_RX_DMA_STREAM, \ - STM32_USART1_RX_DMA_MSK) -#error "invalid DMA stream associated to USART1 RX" -#endif - -#if STM32_UART_USE_USART1 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_TX_DMA_STREAM, \ - STM32_USART1_TX_DMA_MSK) -#error "invalid DMA stream associated to USART1 TX" -#endif - -#if STM32_UART_USE_USART2 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_RX_DMA_STREAM, \ - STM32_USART2_RX_DMA_MSK) -#error "invalid DMA stream associated to USART2 RX" -#endif - -#if STM32_UART_USE_USART2 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_TX_DMA_STREAM, \ - STM32_USART2_TX_DMA_MSK) -#error "invalid DMA stream associated to USART2 TX" -#endif - -#if STM32_UART_USE_USART3 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_RX_DMA_STREAM, \ - STM32_USART3_RX_DMA_MSK) -#error "invalid DMA stream associated to USART3 RX" -#endif - -#if STM32_UART_USE_USART3 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_TX_DMA_STREAM, \ - STM32_USART3_TX_DMA_MSK) -#error "invalid DMA stream associated to USART3 TX" -#endif - -#if STM32_UART_USE_UART4 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_RX_DMA_STREAM, \ - STM32_UART4_RX_DMA_MSK) -#error "invalid DMA stream associated to UART4 RX" -#endif - -#if STM32_UART_USE_UART4 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_TX_DMA_STREAM, \ - STM32_UART4_TX_DMA_MSK) -#error "invalid DMA stream associated to UART4 TX" -#endif - -#if STM32_UART_USE_UART5 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_RX_DMA_STREAM, \ - STM32_UART5_RX_DMA_MSK) -#error "invalid DMA stream associated to UART5 RX" -#endif - -#if STM32_UART_USE_UART5 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_TX_DMA_STREAM, \ - STM32_UART5_TX_DMA_MSK) -#error "invalid DMA stream associated to UART5 TX" -#endif - -#if STM32_UART_USE_USART6 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_RX_DMA_STREAM, \ - STM32_USART6_RX_DMA_MSK) -#error "invalid DMA stream associated to USART6 RX" -#endif - -#if STM32_UART_USE_USART6 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_TX_DMA_STREAM, \ - STM32_USART6_TX_DMA_MSK) -#error "invalid DMA stream associated to USART6 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief UART driver condition flags type. - */ -typedef uint32_t uartflags_t; - -/** - * @brief Structure representing an UART driver. - */ -typedef struct UARTDriver UARTDriver; - -/** - * @brief Generic UART notification callback type. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -typedef void (*uartcb_t)(UARTDriver *uartp); - -/** - * @brief Character received UART notification callback type. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] c received character - */ -typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c); - -/** - * @brief Receive error UART notification callback type. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] e receive error mask - */ -typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e); - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief End of transmission buffer callback. - */ - uartcb_t txend1_cb; - /** - * @brief Physical end of transmission callback. - */ - uartcb_t txend2_cb; - /** - * @brief Receive buffer filled callback. - */ - uartcb_t rxend_cb; - /** - * @brief Character received while out if the @p UART_RECEIVE state. - */ - uartccb_t rxchar_cb; - /** - * @brief Receive error callback. - */ - uartecb_t rxerr_cb; - /* End of the mandatory fields.*/ - /** - * @brief Bit rate. - */ - uint32_t speed; - /** - * @brief Initialization value for the CR1 register. - */ - uint16_t cr1; - /** - * @brief Initialization value for the CR2 register. - */ - uint16_t cr2; - /** - * @brief Initialization value for the CR3 register. - */ - uint16_t cr3; -} UARTConfig; - -/** - * @brief Structure representing an UART driver. - */ -struct UARTDriver { - /** - * @brief Driver state. - */ - uartstate_t state; - /** - * @brief Transmitter state. - */ - uarttxstate_t txstate; - /** - * @brief Receiver state. - */ - uartrxstate_t rxstate; - /** - * @brief Current configuration data. - */ - const UARTConfig *config; -#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Synchronization flag for transmit operations. - */ - bool early; - /** - * @brief Waiting thread on RX. - */ - thread_reference_t threadrx; - /** - * @brief Waiting thread on TX. - */ - thread_reference_t threadtx; -#endif /* UART_USE_WAIT */ -#if (UART_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* UART_USE_MUTUAL_EXCLUSION */ -#if defined(UART_DRIVER_EXT_FIELDS) - UART_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the USART registers block. - */ - USART_TypeDef *usart; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; - /** - * @brief Receive DMA channel. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief Default receive buffer while into @p UART_RX_IDLE state. - */ - volatile uint16_t rxbuf; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_UART_USE_USART1 && !defined(__DOXYGEN__) -extern UARTDriver UARTD1; -#endif - -#if STM32_UART_USE_USART2 && !defined(__DOXYGEN__) -extern UARTDriver UARTD2; -#endif - -#if STM32_UART_USE_USART3 && !defined(__DOXYGEN__) -extern UARTDriver UARTD3; -#endif - -#if STM32_UART_USE_UART4 && !defined(__DOXYGEN__) -extern UARTDriver UARTD4; -#endif - -#if STM32_UART_USE_UART5 && !defined(__DOXYGEN__) -extern UARTDriver UARTD5; -#endif - -#if STM32_UART_USE_USART6 && !defined(__DOXYGEN__) -extern UARTDriver UARTD6; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void uart_lld_init(void); - void uart_lld_start(UARTDriver *uartp); - void uart_lld_stop(UARTDriver *uartp); - void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf); - size_t uart_lld_stop_send(UARTDriver *uartp); - void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf); - size_t uart_lld_stop_receive(UARTDriver *uartp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_UART */ - -#endif /* _UART_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.c b/os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.c new file mode 100644 index 000000000..6c9cf287d --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.c @@ -0,0 +1,764 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv2/serial_lld.c + * @brief STM32 low level serial driver code. + * + * @addtogroup SERIAL + * @{ + */ + +#include "hal.h" + +#if HAL_USE_SERIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/* For compatibility for those devices without LIN support in the USARTs.*/ +#if !defined(USART_ISR_LBDF) +#define USART_ISR_LBDF 0 +#endif + +#if !defined(USART_CR2_LBDIE) +#define USART_CR2_LBDIE 0 +#endif + +/* STM32L0xx/STM32F7xx ST headers difference.*/ +#if !defined(USART_ISR_LBDF) +#define USART_ISR_LBDF USART_ISR_LBD +#endif + +/* Handling the case where UART4 and UART5 are actually USARTs, this happens + in the STM32F0xx.*/ +#if defined(USART4) +#define UART4 USART4 +#endif + +#if defined(USART5) +#define UART5 USART5 +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USART1 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) +SerialDriver SD1; +#endif + +/** @brief USART2 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) +SerialDriver SD2; +#endif + +/** @brief USART3 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) +SerialDriver SD3; +#endif + +/** @brief UART4 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) +SerialDriver SD4; +#endif + +/** @brief UART5 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) +SerialDriver SD5; +#endif + +/** @brief USART6 serial driver identifier.*/ +#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) +SerialDriver SD6; +#endif + +/** @brief UART7 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) +SerialDriver SD7; +#endif + +/** @brief UART8 serial driver identifier.*/ +#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +SerialDriver SD8; +#endif + +/** @brief LPUART1 serial driver identifier.*/ +#if STM32_SERIAL_USE_LPUART1 || defined(__DOXYGEN__) +SerialDriver LPSD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** @brief Driver default configuration.*/ +static const SerialConfig default_config = +{ + SERIAL_DEFAULT_BITRATE, + 0, + USART_CR2_STOP1_BITS | USART_CR2_LINEN, + 0 +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief USART initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration + */ +static void usart_init(SerialDriver *sdp, const SerialConfig *config) { + USART_TypeDef *u = sdp->usart; + + /* Baud rate setting.*/ +#if STM32_SERIAL_USE_LPUART1 + if ( sdp == &LPSD1 ) + { + u->BRR = (uint32_t)( ( (uint64_t)sdp->clock * 256 ) / config->speed); + } + else +#endif + u->BRR = (uint32_t)(sdp->clock / config->speed); + + /* Note that some bits are enforced.*/ + u->CR2 = config->cr2 | USART_CR2_LBDIE; + u->CR3 = config->cr3 | USART_CR3_EIE; + u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE | + USART_CR1_RXNEIE | USART_CR1_TE | + USART_CR1_RE; + u->ICR = 0xFFFFFFFFU; +} + +/** + * @brief USART de-initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] u pointer to an USART I/O block + */ +static void usart_deinit(USART_TypeDef *u) { + + u->CR1 = 0; + u->CR2 = 0; + u->CR3 = 0; +} + +/** + * @brief Error handling routine. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] isr USART ISR register value + */ +static void set_error(SerialDriver *sdp, uint32_t isr) { + eventflags_t sts = 0; + + if (isr & USART_ISR_ORE) + sts |= SD_OVERRUN_ERROR; + if (isr & USART_ISR_PE) + sts |= SD_PARITY_ERROR; + if (isr & USART_ISR_FE) + sts |= SD_FRAMING_ERROR; + if (isr & USART_ISR_NE) + sts |= SD_NOISE_ERROR; + osalSysLockFromISR(); + chnAddFlagsI(sdp, sts); + osalSysUnlockFromISR(); +} + +/** + * @brief Common IRQ handler. + * + * @param[in] sdp communication channel associated to the USART + */ +static void serve_interrupt(SerialDriver *sdp) { + USART_TypeDef *u = sdp->usart; + uint32_t cr1 = u->CR1; + uint32_t isr; + + /* Reading and clearing status.*/ + isr = u->ISR; + u->ICR = isr; + + /* Error condition detection.*/ + if (isr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE)) + set_error(sdp, isr); + + /* Special case, LIN break detection.*/ + if (isr & USART_ISR_LBDF) { + osalSysLockFromISR(); + chnAddFlagsI(sdp, SD_BREAK_DETECTED); + osalSysUnlockFromISR(); + } + + /* Data available.*/ + if (isr & USART_ISR_RXNE) { + osalSysLockFromISR(); + sdIncomingDataI(sdp, (uint8_t)u->RDR); + osalSysUnlockFromISR(); + } + + /* Transmission buffer empty.*/ + if ((cr1 & USART_CR1_TXEIE) && (isr & USART_ISR_TXE)) { + msg_t b; + osalSysLockFromISR(); + b = oqGetI(&sdp->oqueue); + if (b < MSG_OK) { + chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); + u->CR1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; + } + else + u->TDR = b; + osalSysUnlockFromISR(); + } + + /* Physical transmission end.*/ + if (isr & USART_ISR_TC) { + osalSysLockFromISR(); + if (oqIsEmptyI(&sdp->oqueue)) + chnAddFlagsI(sdp, CHN_TRANSMISSION_END); + u->CR1 = cr1 & ~USART_CR1_TCIE; + osalSysUnlockFromISR(); + } +} + +#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) +static void notify1(io_queue_t *qp) { + + (void)qp; + USART1->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) +static void notify2(io_queue_t *qp) { + + (void)qp; + USART2->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) +static void notify3(io_queue_t *qp) { + + (void)qp; + USART3->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) +static void notify4(io_queue_t *qp) { + + (void)qp; + UART4->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) +static void notify5(io_queue_t *qp) { + + (void)qp; + UART5->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) +static void notify6(io_queue_t *qp) { + + (void)qp; + USART6->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) +static void notify7(io_queue_t *qp) { + + (void)qp; + UART7->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +static void notify8(io_queue_t *qp) { + + (void)qp; + UART8->CR1 |= USART_CR1_TXEIE; +} +#endif + +#if STM32_SERIAL_USE_LPUART1 || defined(__DOXYGEN__) +static void notifylp1(io_queue_t *qp) { + + (void)qp; + LPUART1->CR1 |= USART_CR1_TXEIE; +} +#endif + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) +#if !defined(STM32_USART1_HANDLER) +#error "STM32_USART1_HANDLER not defined" +#endif +/** + * @brief USART1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) +#if !defined(STM32_USART2_HANDLER) +#error "STM32_USART2_HANDLER not defined" +#endif +/** + * @brief USART2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if defined(STM32_USART3_8_HANDLER) +#if STM32_SERIAL_USE_USART3 || STM32_SERIAL_USE_UART4 || \ + STM32_SERIAL_USE_UART5 || STM32_SERIAL_USE_USART6 || \ + STM32_SERIAL_USE_UART7 || STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +/** + * @brief USART2 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART3_8_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + +#if STM32_SERIAL_USE_USART3 + serve_interrupt(&SD3); +#endif +#if STM32_SERIAL_USE_UART4 + serve_interrupt(&SD4); +#endif +#if STM32_SERIAL_USE_UART5 + serve_interrupt(&SD5); +#endif +#if STM32_SERIAL_USE_USART6 + serve_interrupt(&SD6); +#endif + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#else /* !defined(STM32_USART3_8_HANDLER) */ + +#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) +#if !defined(STM32_USART3_HANDLER) +#error "STM32_USART3_HANDLER not defined" +#endif +/** + * @brief USART3 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) +#if !defined(STM32_UART4_HANDLER) +#error "STM32_UART4_HANDLER not defined" +#endif +/** + * @brief UART4 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) +#if !defined(STM32_UART5_HANDLER) +#error "STM32_UART5_HANDLER not defined" +#endif +/** + * @brief UART5 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) +#if !defined(STM32_USART6_HANDLER) +#error "STM32_USART6_HANDLER not defined" +#endif +/** + * @brief USART6 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD6); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#endif /* !defined(STM32_USART3_8_HANDLER) */ + +#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) +#if !defined(STM32_UART7_HANDLER) +#error "STM32_UART7_HANDLER not defined" +#endif +/** + * @brief UART7 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART7_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD7); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +#if !defined(STM32_UART8_HANDLER) +#error "STM32_UART8_HANDLER not defined" +#endif +/** + * @brief UART8 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART8_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&SD8); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +#if STM32_SERIAL_USE_LPUART1 || defined(__DOXYGEN__) +#if !defined(STM32_LPUART1_HANDLER) +#error "STM32_LPUART1_HANDLER not defined" +#endif +/** + * @brief LPUART1 interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_LPUART1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_interrupt(&LPSD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level serial driver initialization. + * + * @notapi + */ +void sd_lld_init(void) { + +#if STM32_SERIAL_USE_USART1 + sdObjectInit(&SD1, NULL, notify1); + SD1.usart = USART1; + SD1.clock = STM32_USART1CLK; +#if defined(STM32_USART1_NUMBER) + nvicEnableVector(STM32_USART1_NUMBER, STM32_SERIAL_USART1_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_USART2 + sdObjectInit(&SD2, NULL, notify2); + SD2.usart = USART2; + SD2.clock = STM32_USART2CLK; +#if defined(STM32_USART2_NUMBER) + nvicEnableVector(STM32_USART2_NUMBER, STM32_SERIAL_USART2_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_USART3 + sdObjectInit(&SD3, NULL, notify3); + SD3.usart = USART3; + SD3.clock = STM32_USART3CLK; +#if defined(STM32_USART3_NUMBER) + nvicEnableVector(STM32_USART3_NUMBER, STM32_SERIAL_USART3_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_UART4 + sdObjectInit(&SD4, NULL, notify4); + SD4.usart = UART4; + SD4.clock = STM32_UART4CLK; +#if defined(STM32_UART4_NUMBER) + nvicEnableVector(STM32_UART4_NUMBER, STM32_SERIAL_UART4_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_UART5 + sdObjectInit(&SD5, NULL, notify5); + SD5.usart = UART5; + SD5.clock = STM32_UART5CLK; +#if defined(STM32_UART5_NUMBER) + nvicEnableVector(STM32_UART5_NUMBER, STM32_SERIAL_UART5_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_USART6 + sdObjectInit(&SD6, NULL, notify6); + SD6.usart = USART6; + SD6.clock = STM32_USART6CLK; +#if defined(STM32_USART6_NUMBER) + nvicEnableVector(STM32_USART6_NUMBER, STM32_SERIAL_USART6_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_UART7 + sdObjectInit(&SD7, NULL, notify7); + SD7.usart = UART7; + SD7.clock = STM32_UART7CLK; +#if defined(STM32_UART7_NUMBER) + nvicEnableVector(STM32_UART7_NUMBER, STM32_SERIAL_UART7_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_UART8 + sdObjectInit(&SD8, NULL, notify8); + SD8.usart = UART8; + SD8.clock = STM32_UART8CLK; +#if defined(STM32_UART8_NUMBER) + nvicEnableVector(STM32_UART8_NUMBER, STM32_SERIAL_UART8_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_LPUART1 + sdObjectInit(&LPSD1, NULL, notifylp1); + LPSD1.usart = LPUART1; + LPSD1.clock = STM32_LPUART1CLK; +#if defined(STM32_LPUART1_NUMBER) + nvicEnableVector(STM32_LPUART1_NUMBER, STM32_SERIAL_LPUART1_PRIORITY); +#endif +#endif + +#if STM32_SERIAL_USE_USART3 || STM32_SERIAL_USE_UART4 || \ + STM32_SERIAL_USE_UART5 || STM32_SERIAL_USE_USART6 || \ + STM32_SERIAL_USE_UART7 || STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) +#if defined(STM32_USART3_8_HANDLER) + nvicEnableVector(STM32_USART3_8_NUMBER, STM32_SERIAL_USART3_8_PRIORITY); +#endif +#endif +} + +/** + * @brief Low level serial driver configuration and (re)start. + * + * @param[in] sdp pointer to a @p SerialDriver object + * @param[in] config the architecture-dependent serial driver configuration. + * If this parameter is set to @p NULL then a default + * configuration is used. + * + * @notapi + */ +void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { + + if (config == NULL) + config = &default_config; + + if (sdp->state == SD_STOP) { +#if STM32_SERIAL_USE_USART1 + if (&SD1 == sdp) { + rccEnableUSART1(FALSE); + } +#endif +#if STM32_SERIAL_USE_USART2 + if (&SD2 == sdp) { + rccEnableUSART2(FALSE); + } +#endif +#if STM32_SERIAL_USE_USART3 + if (&SD3 == sdp) { + rccEnableUSART3(FALSE); + } +#endif +#if STM32_SERIAL_USE_UART4 + if (&SD4 == sdp) { + rccEnableUART4(FALSE); + } +#endif +#if STM32_SERIAL_USE_UART5 + if (&SD5 == sdp) { + rccEnableUART5(FALSE); + } +#endif +#if STM32_SERIAL_USE_USART6 + if (&SD6 == sdp) { + rccEnableUSART6(FALSE); + } +#endif +#if STM32_SERIAL_USE_UART7 + if (&SD7 == sdp) { + rccEnableUART7(FALSE); + } +#endif +#if STM32_SERIAL_USE_UART8 + if (&SD8 == sdp) { + rccEnableUART8(FALSE); + } +#endif +#if STM32_SERIAL_USE_LPUART1 + if (&LPSD1 == sdp) { + rccEnableLPUART1(FALSE); + } +#endif + } + usart_init(sdp, config); +} + +/** + * @brief Low level serial driver stop. + * @details De-initializes the USART, stops the associated clock, resets the + * interrupt vector. + * + * @param[in] sdp pointer to a @p SerialDriver object + * + * @notapi + */ +void sd_lld_stop(SerialDriver *sdp) { + + if (sdp->state == SD_READY) { + /* UART is de-initialized then clocks are disabled.*/ + usart_deinit(sdp->usart); + +#if STM32_SERIAL_USE_USART1 + if (&SD1 == sdp) { + rccDisableUSART1(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_USART2 + if (&SD2 == sdp) { + rccDisableUSART2(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_USART3 + if (&SD3 == sdp) { + rccDisableUSART3(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_UART4 + if (&SD4 == sdp) { + rccDisableUART4(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_UART5 + if (&SD5 == sdp) { + rccDisableUART5(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_USART6 + if (&SD6 == sdp) { + rccDisableUSART6(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_UART7 + if (&SD7 == sdp) { + rccDisableUART7(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_UART8 + if (&SD8 == sdp) { + rccDisableUART8(FALSE); + return; + } +#endif +#if STM32_SERIAL_USE_LPUART1 + if (&LPSD1 == sdp) { + rccDisableLPUART1(FALSE); + return; + } +#endif + } +} + +#endif /* HAL_USE_SERIAL */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.h b/os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.h new file mode 100644 index 000000000..478395030 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv2/hal_serial_lld.h @@ -0,0 +1,399 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv2/serial_lld.h + * @brief STM32 low level serial driver header. + * + * @addtogroup SERIAL + * @{ + */ + +#ifndef _SERIAL_LLD_H_ +#define _SERIAL_LLD_H_ + +#if HAL_USE_SERIAL || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief USART1 driver enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART1) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART1 FALSE +#endif + +/** + * @brief USART2 driver enable switch. + * @details If set to @p TRUE the support for USART2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART2) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART2 FALSE +#endif + +/** + * @brief USART3 driver enable switch. + * @details If set to @p TRUE the support for USART3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART3) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART3 FALSE +#endif + +/** + * @brief UART4 driver enable switch. + * @details If set to @p TRUE the support for UART4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART4) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART4 FALSE +#endif + +/** + * @brief UART5 driver enable switch. + * @details If set to @p TRUE the support for UART5 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART5) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART5 FALSE +#endif + +/** + * @brief USART6 driver enable switch. + * @details If set to @p TRUE the support for USART6 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_USART6) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_USART6 FALSE +#endif + +/** + * @brief UART7 driver enable switch. + * @details If set to @p TRUE the support for UART7 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART7) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART7 FALSE +#endif + +/** + * @brief UART8 driver enable switch. + * @details If set to @p TRUE the support for UART8 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_UART8) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_UART8 FALSE +#endif + +/** + * @brief LPUART1 driver enable switch. + * @details If set to @p TRUE the support for LPUART is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_SERIAL_USE_LPUART1) || defined(__DOXYGEN__) +#define STM32_SERIAL_USE_LPUART1 FALSE +#endif + +/** + * @brief USART1 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART1_PRIORITY 12 +#endif + +/** + * @brief USART2 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART2_PRIORITY 12 +#endif + +/** + * @brief USART3 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART3_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART3_PRIORITY 12 +#endif + +/** + * @brief USART3..8 interrupt priority level setting. + * @note Only valid on those devices with a shared IRQ. + */ +#if !defined(STM32_SERIAL_USART3_8_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART3_8_PRIORITY 12 +#endif + +/** + * @brief UART4 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART4_PRIORITY 12 +#endif + +/** + * @brief UART5 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART5_PRIORITY 12 +#endif + +/** + * @brief USART6 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_USART6_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_USART6_PRIORITY 12 +#endif + +/** + * @brief UART7 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART7_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART7_PRIORITY 12 +#endif + +/** + * @brief UART8 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_UART8_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_UART8_PRIORITY 12 +#endif + +/** + * @brief LPUART1 interrupt priority level setting. + */ +#if !defined(STM32_SERIAL_LPUART1_PRIORITY) || defined(__DOXYGEN__) +#define STM32_SERIAL_LPUART1_PRIORITY 12 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_SERIAL_USE_USART1 && !STM32_HAS_USART1 +#error "USART1 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_USART2 && !STM32_HAS_USART2 +#error "USART2 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_USART3 && !STM32_HAS_USART3 +#error "USART3 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART4 && !STM32_HAS_UART4 +#error "UART4 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART5 && !STM32_HAS_UART5 +#error "UART5 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_USART6 && !STM32_HAS_USART6 +#error "USART6 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART7 && !STM32_HAS_UART7 +#error "UART7 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_UART8 && !STM32_HAS_UART8 +#error "UART8 not present in the selected device" +#endif + +#if STM32_SERIAL_USE_LPUART1 && !STM32_HAS_LPUART1 +#error "LPUART1 not present in the selected device" +#endif + +#if !STM32_SERIAL_USE_USART1 && !STM32_SERIAL_USE_USART2 && \ + !STM32_SERIAL_USE_USART3 && !STM32_SERIAL_USE_UART4 && \ + !STM32_SERIAL_USE_UART5 && !STM32_SERIAL_USE_USART6 && \ + !STM32_SERIAL_USE_UART7 && !STM32_SERIAL_USE_UART8 && \ + !STM32_SERIAL_USE_LPUART1 +#error "SERIAL driver activated but no USART/UART peripheral assigned" +#endif + +#if STM32_SERIAL_USE_USART1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART1_PRIORITY) +#error "Invalid IRQ priority assigned to USART1" +#endif + +#if STM32_SERIAL_USE_USART2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART2_PRIORITY) +#error "Invalid IRQ priority assigned to USART2" +#endif + +#if STM32_SERIAL_USE_USART3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART3_PRIORITY) +#error "Invalid IRQ priority assigned to USART3" +#endif + +#if STM32_SERIAL_USE_UART4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART4_PRIORITY) +#error "Invalid IRQ priority assigned to UART4" +#endif + +#if STM32_SERIAL_USE_UART5 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART5_PRIORITY) +#error "Invalid IRQ priority assigned to UART5" +#endif + +#if STM32_SERIAL_USE_USART6 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART6_PRIORITY) +#error "Invalid IRQ priority assigned to USART6" +#endif + +#if STM32_SERIAL_USE_UART7 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART7_PRIORITY) +#error "Invalid IRQ priority assigned to UART7" +#endif + +#if STM32_SERIAL_USE_UART8 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART8_PRIORITY) +#error "Invalid IRQ priority assigned to UART8" +#endif + +#if STM32_SERIAL_USE_LPUART1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_LPUART1_PRIORITY) +#error "Invalid IRQ priority assigned to LPUART1" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief STM32 Serial Driver configuration structure. + * @details An instance of this structure must be passed to @p sdStart() + * in order to configure and start a serial driver operations. + * @note This structure content is architecture dependent, each driver + * implementation defines its own version and the custom static + * initializers. + */ +typedef struct { + /** + * @brief Bit rate. + */ + uint32_t speed; + /* End of the mandatory fields.*/ + /** + * @brief Initialization value for the CR1 register. + */ + uint32_t cr1; + /** + * @brief Initialization value for the CR2 register. + */ + uint32_t cr2; + /** + * @brief Initialization value for the CR3 register. + */ + uint32_t cr3; +} SerialConfig; + +/** + * @brief @p SerialDriver specific data. + */ +#define _serial_driver_data \ + _base_asynchronous_channel_data \ + /* Driver state.*/ \ + sdstate_t state; \ + /* Input queue.*/ \ + input_queue_t iqueue; \ + /* Output queue.*/ \ + output_queue_t oqueue; \ + /* Input circular buffer.*/ \ + uint8_t ib[SERIAL_BUFFERS_SIZE]; \ + /* Output circular buffer.*/ \ + uint8_t ob[SERIAL_BUFFERS_SIZE]; \ + /* End of the mandatory fields.*/ \ + /* Pointer to the USART registers block.*/ \ + USART_TypeDef *usart; \ + /* Clock frequency for the associated USART/UART.*/ \ + uint32_t clock; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/* + * Extra USARTs definitions here (missing from the ST header file). + */ +#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/ +#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/ +#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/ +#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_SERIAL_USE_USART1 && !defined(__DOXYGEN__) +extern SerialDriver SD1; +#endif +#if STM32_SERIAL_USE_USART2 && !defined(__DOXYGEN__) +extern SerialDriver SD2; +#endif +#if STM32_SERIAL_USE_USART3 && !defined(__DOXYGEN__) +extern SerialDriver SD3; +#endif +#if STM32_SERIAL_USE_UART4 && !defined(__DOXYGEN__) +extern SerialDriver SD4; +#endif +#if STM32_SERIAL_USE_UART5 && !defined(__DOXYGEN__) +extern SerialDriver SD5; +#endif +#if STM32_SERIAL_USE_USART6 && !defined(__DOXYGEN__) +extern SerialDriver SD6; +#endif +#if STM32_SERIAL_USE_UART7 && !defined(__DOXYGEN__) +extern SerialDriver SD7; +#endif +#if STM32_SERIAL_USE_UART8 && !defined(__DOXYGEN__) +extern SerialDriver SD8; +#endif +#if STM32_SERIAL_USE_LPUART1 && !defined(__DOXYGEN__) +extern SerialDriver LPSD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void sd_lld_init(void); + void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); + void sd_lld_stop(SerialDriver *sdp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_SERIAL */ + +#endif /* _SERIAL_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.c b/os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.c new file mode 100644 index 000000000..f72effb67 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.c @@ -0,0 +1,931 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv2/uart_lld.c + * @brief STM32 low level UART driver code. + * + * @addtogroup UART + * @{ + */ + +#include "hal.h" + +#if HAL_USE_UART || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/* STM32L0xx/STM32F7xx ST headers difference.*/ +#if !defined(USART_ISR_LBDF) +#define USART_ISR_LBDF USART_ISR_LBD +#endif + +#define USART1_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART1_RX_DMA_STREAM, \ + STM32_USART1_RX_DMA_CHN) + +#define USART1_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART1_TX_DMA_STREAM, \ + STM32_USART1_TX_DMA_CHN) + +#define USART2_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART2_RX_DMA_STREAM, \ + STM32_USART2_RX_DMA_CHN) + +#define USART2_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART2_TX_DMA_STREAM, \ + STM32_USART2_TX_DMA_CHN) + +#define USART3_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART3_RX_DMA_STREAM, \ + STM32_USART3_RX_DMA_CHN) + +#define USART3_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART3_TX_DMA_STREAM, \ + STM32_USART3_TX_DMA_CHN) + +#define UART4_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART4_RX_DMA_STREAM, \ + STM32_UART4_RX_DMA_CHN) + +#define UART4_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART4_TX_DMA_STREAM, \ + STM32_UART4_TX_DMA_CHN) + +#define UART5_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART5_RX_DMA_STREAM, \ + STM32_UART5_RX_DMA_CHN) + +#define UART5_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART5_TX_DMA_STREAM, \ + STM32_UART5_TX_DMA_CHN) + +#define USART6_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART6_RX_DMA_STREAM, \ + STM32_USART6_RX_DMA_CHN) + +#define USART6_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_USART6_TX_DMA_STREAM, \ + STM32_USART6_TX_DMA_CHN) + +#define UART7_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART7_RX_DMA_STREAM, \ + STM32_UART7_RX_DMA_CHN) + +#define UART7_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART7_TX_DMA_STREAM, \ + STM32_UART7_TX_DMA_CHN) + +#define UART8_RX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART8_RX_DMA_STREAM, \ + STM32_UART8_RX_DMA_CHN) + +#define UART8_TX_DMA_CHANNEL \ + STM32_DMA_GETCHANNEL(STM32_UART_UART8_TX_DMA_STREAM, \ + STM32_UART8_TX_DMA_CHN) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USART1 UART driver identifier.*/ +#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) +UARTDriver UARTD1; +#endif + +/** @brief USART2 UART driver identifier.*/ +#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) +UARTDriver UARTD2; +#endif + +/** @brief USART3 UART driver identifier.*/ +#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) +UARTDriver UARTD3; +#endif + +/** @brief UART4 UART driver identifier.*/ +#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) +UARTDriver UARTD4; +#endif + +/** @brief UART5 UART driver identifier.*/ +#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) +UARTDriver UARTD5; +#endif + +/** @brief USART6 UART driver identifier.*/ +#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) +UARTDriver UARTD6; +#endif + +/** @brief UART7 UART driver identifier.*/ +#if STM32_UART_USE_UART7 || defined(__DOXYGEN__) +UARTDriver UARTD7; +#endif + +/** @brief UART8 UART driver identifier.*/ +#if STM32_UART_USE_UART8 || defined(__DOXYGEN__) +UARTDriver UARTD8; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Status bits translation. + * + * @param[in] sr USART SR register value + * + * @return The error flags. + */ +static uartflags_t translate_errors(uint32_t isr) { + uartflags_t sts = 0; + + if (isr & USART_ISR_ORE) + sts |= UART_OVERRUN_ERROR; + if (isr & USART_ISR_PE) + sts |= UART_PARITY_ERROR; + if (isr & USART_ISR_FE) + sts |= UART_FRAMING_ERROR; + if (isr & USART_ISR_NE) + sts |= UART_NOISE_ERROR; + if (isr & USART_ISR_LBDF) + sts |= UART_BREAK_DETECTED; + return sts; +} + +/** + * @brief Puts the receiver in the UART_RX_IDLE state. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void uart_enter_rx_idle_loop(UARTDriver *uartp) { + uint32_t mode; + + /* RX DMA channel preparation, if the char callback is defined then the + TCIE interrupt is enabled too.*/ + if (uartp->config->rxchar_cb == NULL) + mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC; + else + mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE; + dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf); + dmaStreamSetTransactionSize(uartp->dmarx, 1); + dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode); + dmaStreamEnable(uartp->dmarx); +} + +/** + * @brief USART de-initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void usart_stop(UARTDriver *uartp) { + + /* Stops RX and TX DMA channels.*/ + dmaStreamDisable(uartp->dmarx); + dmaStreamDisable(uartp->dmatx); + + /* Stops USART operations.*/ + uartp->usart->CR1 = 0; + uartp->usart->CR2 = 0; + uartp->usart->CR3 = 0; +} + +/** + * @brief USART initialization. + * @details This function must be invoked with interrupts disabled. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void usart_start(UARTDriver *uartp) { + uint32_t cr1; + USART_TypeDef *u = uartp->usart; + + /* Defensive programming, starting from a clean state.*/ + usart_stop(uartp); + + /* Baud rate setting.*/ + u->BRR = (uint32_t)(uartp->clock / uartp->config->speed); + + /* Resetting eventual pending status flags.*/ + u->ICR = 0xFFFFFFFFU; + + /* Note that some bits are enforced because required for correct driver + operations.*/ + u->CR2 = uartp->config->cr2 | USART_CR2_LBDIE; + u->CR3 = uartp->config->cr3 | USART_CR3_DMAT | USART_CR3_DMAR | + USART_CR3_EIE; + + /* Mustn't ever set TCIE here - if done, it causes an immediate + interrupt.*/ + cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE; + u->CR1 = uartp->config->cr1 | cr1; + + /* Starting the receiver idle loop.*/ + uart_enter_rx_idle_loop(uartp); +} + +/** + * @brief RX DMA common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_UART_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_UART_DMA_ERROR_HOOK(uartp); + } +#else + (void)flags; +#endif + + if (uartp->rxstate == UART_RX_IDLE) { + /* Receiver in idle state, a callback is generated, if enabled, for each + received character and then the driver stays in the same state.*/ + _uart_rx_idle_code(uartp); + } + else { + /* Receiver in active state, a callback is generated, if enabled, after + a completed transfer.*/ + dmaStreamDisable(uartp->dmarx); + _uart_rx_complete_isr_code(uartp); + } +} + +/** + * @brief TX DMA common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] flags pre-shifted content of the ISR register + */ +static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) { + + /* DMA errors handling.*/ +#if defined(STM32_UART_DMA_ERROR_HOOK) + if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { + STM32_UART_DMA_ERROR_HOOK(uartp); + } +#else + (void)flags; +#endif + + dmaStreamDisable(uartp->dmatx); + + /* A callback is generated, if enabled, after a completed transfer.*/ + _uart_tx1_isr_code(uartp); +} + +/** + * @brief USART common service routine. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +static void serve_usart_irq(UARTDriver *uartp) { + uint32_t isr; + USART_TypeDef *u = uartp->usart; + uint32_t cr1 = u->CR1; + + /* Reading and clearing status.*/ + isr = u->ISR; + u->ICR = isr; + + if (isr & (USART_ISR_LBDF | USART_ISR_ORE | USART_ISR_NE | + USART_ISR_FE | USART_ISR_PE)) { + _uart_rx_error_isr_code(uartp, translate_errors(isr)); + } + + if ((isr & USART_ISR_TC) && (cr1 & USART_CR1_TCIE)) { + /* TC interrupt disabled.*/ + u->CR1 = cr1 & ~USART_CR1_TCIE; + + /* End of transmission, a callback is generated.*/ + _uart_tx2_isr_code(uartp); + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) +#if !defined(STM32_USART1_HANDLER) +#error "STM32_USART1_HANDLER not defined" +#endif +/** + * @brief USART1 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD1); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART1 */ + +#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) +#if !defined(STM32_USART2_HANDLER) +#error "STM32_USART2_HANDLER not defined" +#endif +/** + * @brief USART2 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD2); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART2 */ + +#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) +#if !defined(STM32_USART3_HANDLER) +#error "STM32_USART3_HANDLER not defined" +#endif +/** + * @brief USART3 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD3); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART3 */ + +#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) +#if !defined(STM32_UART4_HANDLER) +#error "STM32_UART4_HANDLER not defined" +#endif +/** + * @brief UART4 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD4); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_UART4 */ + +#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) +#if !defined(STM32_UART5_HANDLER) +#error "STM32_UART5_HANDLER not defined" +#endif +/** + * @brief UART5 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD5); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_UART5 */ + +#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) +#if !defined(STM32_USART6_HANDLER) +#error "STM32_USART6_HANDLER not defined" +#endif +/** + * @brief USART6 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD6); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_USART6 */ + +#if STM32_UART_USE_UART7 || defined(__DOXYGEN__) +#if !defined(STM32_UART7_HANDLER) +#error "STM32_UART7_HANDLER not defined" +#endif +/** + * @brief UART7 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART7_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD7); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_UART7 */ + +#if STM32_UART_USE_UART8 || defined(__DOXYGEN__) +#if !defined(STM32_UART8_HANDLER) +#error "STM32_UART8_HANDLER not defined" +#endif +/** + * @brief UART8 IRQ handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_UART8_HANDLER) { + + OSAL_IRQ_PROLOGUE(); + + serve_usart_irq(&UARTD8); + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_UART_USE_UART8 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level UART driver initialization. + * + * @notapi + */ +void uart_lld_init(void) { + +#if STM32_UART_USE_USART1 + uartObjectInit(&UARTD1); + UARTD1.usart = USART1; + UARTD1.clock = STM32_USART1CLK; + UARTD1.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD1.dmarx = STM32_DMA_STREAM(STM32_UART_USART1_RX_DMA_STREAM); + UARTD1.dmatx = STM32_DMA_STREAM(STM32_UART_USART1_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_USART2 + uartObjectInit(&UARTD2); + UARTD2.usart = USART2; + UARTD2.clock = STM32_USART2CLK; + UARTD2.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD2.dmarx = STM32_DMA_STREAM(STM32_UART_USART2_RX_DMA_STREAM); + UARTD2.dmatx = STM32_DMA_STREAM(STM32_UART_USART2_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_USART3 + uartObjectInit(&UARTD3); + UARTD3.usart = USART3; + UARTD3.clock = STM32_USART3CLK; + UARTD3.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD3.dmarx = STM32_DMA_STREAM(STM32_UART_USART3_RX_DMA_STREAM); + UARTD3.dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_UART4 + uartObjectInit(&UARTD4); + UARTD4.usart = UART4; + UARTD4.clock = STM32_UART4CLK; + UARTD4.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD4.dmarx = STM32_DMA_STREAM(STM32_UART_UART4_RX_DMA_STREAM); + UARTD4.dmatx = STM32_DMA_STREAM(STM32_UART_UART4_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_UART5 + uartObjectInit(&UARTD5); + UARTD5.usart = UART5; + UARTD5.clock = STM32_UART5CLK; + UARTD5.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD5.dmarx = STM32_DMA_STREAM(STM32_UART_UART5_RX_DMA_STREAM); + UARTD5.dmatx = STM32_DMA_STREAM(STM32_UART_UART5_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_USART6 + uartObjectInit(&UARTD6); + UARTD6.usart = USART6; + UARTD6.clock = STM32_USART6CLK; + UARTD6.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD6.dmarx = STM32_DMA_STREAM(STM32_UART_USART6_RX_DMA_STREAM); + UARTD6.dmatx = STM32_DMA_STREAM(STM32_UART_USART6_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_UART7 + uartObjectInit(&UARTD7); + UARTD7.usart = UART7; + UARTD7.clock = STM32_UART7CLK; + UARTD7.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD7.dmarx = STM32_DMA_STREAM(STM32_UART_UART7_RX_DMA_STREAM); + UARTD7.dmatx = STM32_DMA_STREAM(STM32_UART_UART7_TX_DMA_STREAM); +#endif + +#if STM32_UART_USE_UART8 + uartObjectInit(&UARTD8); + UARTD8.usart = UART8; + UARTD8.clock = STM32_UART8CLK; + UARTD8.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; + UARTD8.dmarx = STM32_DMA_STREAM(STM32_UART_UART8_RX_DMA_STREAM); + UARTD8.dmatx = STM32_DMA_STREAM(STM32_UART_UART8_TX_DMA_STREAM); +#endif +} + +/** + * @brief Configures and activates the UART peripheral. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +void uart_lld_start(UARTDriver *uartp) { + + if (uartp->state == UART_STOP) { +#if STM32_UART_USE_USART1 + if (&UARTD1 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART1_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART1_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART1(FALSE); + nvicEnableVector(STM32_USART1_NUMBER, STM32_UART_USART1_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART1_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART1_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_USART2 + if (&UARTD2 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART2_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART2_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART2(FALSE); + nvicEnableVector(STM32_USART2_NUMBER, STM32_UART_USART2_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART2_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART2_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_USART3 + if (&UARTD3 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART3_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART3_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART3(FALSE); + nvicEnableVector(STM32_USART3_NUMBER, STM32_UART_USART3_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART3_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_UART4 + if (&UARTD4 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_UART4_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_UART4_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUART4(FALSE); + nvicEnableVector(STM32_UART4_NUMBER, STM32_UART_UART4_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(UART4_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_UART4_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_UART5 + if (&UARTD5 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_UART5_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_UART5_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUART5(FALSE); + nvicEnableVector(STM32_UART5_NUMBER, STM32_UART_UART5_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(UART5_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_UART5_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_USART6 + if (&UARTD6 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_USART6_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_USART6_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUSART6(FALSE); + nvicEnableVector(STM32_USART6_NUMBER, STM32_UART_USART6_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(USART6_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_USART6_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_UART7 + if (&UARTD7 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_UART7_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_UART7_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUART7(FALSE); + nvicEnableVector(STM32_UART7_NUMBER, STM32_UART_UART7_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(UART7_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_UART7_DMA_PRIORITY); + } +#endif + +#if STM32_UART_USE_UART8 + if (&UARTD8 == uartp) { + bool b; + b = dmaStreamAllocate(uartp->dmarx, + STM32_UART_UART8_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + b = dmaStreamAllocate(uartp->dmatx, + STM32_UART_UART8_IRQ_PRIORITY, + (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, + (void *)uartp); + osalDbgAssert(!b, "stream already allocated"); + rccEnableUART8(FALSE); + nvicEnableVector(STM32_UART8_NUMBER, STM32_UART_UART8_IRQ_PRIORITY); + uartp->dmamode |= STM32_DMA_CR_CHSEL(UART8_RX_DMA_CHANNEL) | + STM32_DMA_CR_PL(STM32_UART_UART8_DMA_PRIORITY); + } +#endif + + /* Static DMA setup, the transfer size depends on the USART settings, + it is 16 bits if M=1 and PCE=0 else it is 8 bits.*/ + if ((uartp->config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_M) + uartp->dmamode |= STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; + dmaStreamSetPeripheral(uartp->dmarx, &uartp->usart->RDR); + dmaStreamSetPeripheral(uartp->dmatx, &uartp->usart->TDR); + uartp->rxbuf = 0; + } + + uartp->rxstate = UART_RX_IDLE; + uartp->txstate = UART_TX_IDLE; + usart_start(uartp); +} + +/** + * @brief Deactivates the UART peripheral. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @notapi + */ +void uart_lld_stop(UARTDriver *uartp) { + + if (uartp->state == UART_READY) { + usart_stop(uartp); + dmaStreamRelease(uartp->dmarx); + dmaStreamRelease(uartp->dmatx); + +#if STM32_UART_USE_USART1 + if (&UARTD1 == uartp) { + nvicDisableVector(STM32_USART1_NUMBER); + rccDisableUSART1(FALSE); + return; + } +#endif + +#if STM32_UART_USE_USART2 + if (&UARTD2 == uartp) { + nvicDisableVector(STM32_USART2_NUMBER); + rccDisableUSART2(FALSE); + return; + } +#endif + +#if STM32_UART_USE_USART3 + if (&UARTD3 == uartp) { + nvicDisableVector(STM32_USART3_NUMBER); + rccDisableUSART3(FALSE); + return; + } +#endif + +#if STM32_UART_USE_UART4 + if (&UARTD4 == uartp) { + nvicDisableVector(STM32_UART4_NUMBER); + rccDisableUART4(FALSE); + return; + } +#endif + +#if STM32_UART_USE_UART5 + if (&UARTD5 == uartp) { + nvicDisableVector(STM32_UART5_NUMBER); + rccDisableUART5(FALSE); + return; + } +#endif + +#if STM32_UART_USE_USART6 + if (&UARTD6 == uartp) { + nvicDisableVector(STM32_USART6_NUMBER); + rccDisableUSART6(FALSE); + return; + } +#endif + +#if STM32_UART_USE_UART7 + if (&UARTD7 == uartp) { + nvicDisableVector(STM32_UART7_NUMBER); + rccDisableUART7(FALSE); + return; + } +#endif + +#if STM32_UART_USE_UART8 + if (&UARTD8 == uartp) { + nvicDisableVector(STM32_UART8_NUMBER); + rccDisableUART8(FALSE); + return; + } +#endif + } +} + +/** + * @brief Starts a transmission on the UART peripheral. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] n number of data frames to send + * @param[in] txbuf the pointer to the transmit buffer + * + * @notapi + */ +void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) { + + /* TX DMA channel preparation.*/ + dmaStreamSetMemory0(uartp->dmatx, txbuf); + dmaStreamSetTransactionSize(uartp->dmatx, n); + dmaStreamSetMode(uartp->dmatx, uartp->dmamode | STM32_DMA_CR_DIR_M2P | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + + /* Only enable TC interrupt if there's a callback attached to it. + Also we need to clear TC flag which could be set before. */ + if (uartp->config->txend2_cb != NULL) { + uartp->usart->ICR = USART_ICR_TCCF; + uartp->usart->CR1 |= USART_CR1_TCIE; + } + + /* Starting transfer.*/ + dmaStreamEnable(uartp->dmatx); +} + +/** + * @brief Stops any ongoing transmission. + * @note Stopping a transmission also suppresses the transmission callbacks. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @return The number of data frames not transmitted by the + * stopped transmit operation. + * + * @notapi + */ +size_t uart_lld_stop_send(UARTDriver *uartp) { + + dmaStreamDisable(uartp->dmatx); + + return dmaStreamGetTransactionSize(uartp->dmatx); +} + +/** + * @brief Starts a receive operation on the UART peripheral. + * @note The buffers are organized as uint8_t arrays for data sizes below + * or equal to 8 bits else it is organized as uint16_t arrays. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] n number of data frames to send + * @param[out] rxbuf the pointer to the receive buffer + * + * @notapi + */ +void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) { + + /* Stopping previous activity (idle state).*/ + dmaStreamDisable(uartp->dmarx); + + /* RX DMA channel preparation.*/ + dmaStreamSetMemory0(uartp->dmarx, rxbuf); + dmaStreamSetTransactionSize(uartp->dmarx, n); + dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M | + STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); + + /* Starting transfer.*/ + dmaStreamEnable(uartp->dmarx); +} + +/** + * @brief Stops any ongoing receive operation. + * @note Stopping a receive operation also suppresses the receive callbacks. + * + * @param[in] uartp pointer to the @p UARTDriver object + * + * @return The number of data frames not received by the + * stopped receive operation. + * + * @notapi + */ +size_t uart_lld_stop_receive(UARTDriver *uartp) { + size_t n; + + dmaStreamDisable(uartp->dmarx); + n = dmaStreamGetTransactionSize(uartp->dmarx); + uart_enter_rx_idle_loop(uartp); + + return n; +} + +#endif /* HAL_USE_UART */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.h b/os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.h new file mode 100644 index 000000000..3b3c8ce00 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USARTv2/hal_uart_lld.h @@ -0,0 +1,734 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USARTv2/uart_lld.h + * @brief STM32 low level UART driver header. + * + * @addtogroup UART + * @{ + */ + +#ifndef _UART_LLD_H_ +#define _UART_LLD_H_ + +#if HAL_USE_UART || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief UART driver on USART1 enable switch. + * @details If set to @p TRUE the support for USART1 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART1) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART1 FALSE +#endif + +/** + * @brief UART driver on USART2 enable switch. + * @details If set to @p TRUE the support for USART2 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART2) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART2 FALSE +#endif + +/** + * @brief UART driver on USART3 enable switch. + * @details If set to @p TRUE the support for USART3 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART3) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART3 FALSE +#endif + +/** + * @brief UART driver on UART4 enable switch. + * @details If set to @p TRUE the support for UART4 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_UART4) || defined(__DOXYGEN__) +#define STM32_UART_USE_UART4 FALSE +#endif + +/** + * @brief UART driver on UART5 enable switch. + * @details If set to @p TRUE the support for UART5 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_UART5) || defined(__DOXYGEN__) +#define STM32_UART_USE_UART5 FALSE +#endif + +/** + * @brief UART driver on USART6 enable switch. + * @details If set to @p TRUE the support for USART6 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_USART6) || defined(__DOXYGEN__) +#define STM32_UART_USE_USART6 FALSE +#endif + +/** + * @brief UART driver on UART7 enable switch. + * @details If set to @p TRUE the support for UART7 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_UART7) || defined(__DOXYGEN__) +#define STM32_UART_USE_UART7 FALSE +#endif + +/** + * @brief UART driver on UART8 enable switch. + * @details If set to @p TRUE the support for UART8 is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_UART_USE_UART8) || defined(__DOXYGEN__) +#define STM32_UART_USE_UART8 FALSE +#endif + +/** + * @brief USART1 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART1_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART1_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART2 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART2_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART2_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART3 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART3_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART3_IRQ_PRIORITY 12 +#endif + +/** + * @brief UART4 interrupt priority level setting. + */ +#if !defined(STM32_UART_UART4_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART4_IRQ_PRIORITY 12 +#endif + +/** + * @brief UART5 interrupt priority level setting. + */ +#if !defined(STM32_UART_UART5_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART5_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART6 interrupt priority level setting. + */ +#if !defined(STM32_UART_USART6_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART6_IRQ_PRIORITY 12 +#endif + +/** + * @brief UART7 interrupt priority level setting. + */ +#if !defined(STM32_UART_UART7_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART7_IRQ_PRIORITY 12 +#endif + +/** + * @brief UART8 interrupt priority level setting. + */ +#if !defined(STM32_UART_UART8_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART8_IRQ_PRIORITY 12 +#endif + +/** + * @brief USART1 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART1_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART1_DMA_PRIORITY 0 +#endif + +/** + * @brief USART2 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART2_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART2_DMA_PRIORITY 0 +#endif + +/** + * @brief USART3 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART3_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART3_DMA_PRIORITY 0 +#endif + +/** + * @brief UART4 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_UART4_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART4_DMA_PRIORITY 0 +#endif + +/** + * @brief UART5 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_UART5_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART5_DMA_PRIORITY 0 +#endif + +/** + * @brief USART6 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_USART6_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_USART6_DMA_PRIORITY 0 +#endif + +/** + * @brief UART7 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_UART7_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART7_DMA_PRIORITY 0 +#endif + +/** + * @brief UART8 DMA priority (0..3|lowest..highest). + * @note The priority level is used for both the TX and RX DMA channels but + * because of the channels ordering the RX channel has always priority + * over the TX channel. + */ +#if !defined(STM32_UART_UART8_DMA_PRIORITY) || defined(__DOXYGEN__) +#define STM32_UART_UART8_DMA_PRIORITY 0 +#endif + +/** + * @brief UART DMA error hook. + * @note The default action for DMA errors is a system halt because DMA + * error can only happen because programming errors. + */ +#if !defined(STM32_UART_DMA_ERROR_HOOK) || defined(__DOXYGEN__) +#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_UART_USE_USART1 && !STM32_HAS_USART1 +#error "USART1 not present in the selected device" +#endif + +#if STM32_UART_USE_USART2 && !STM32_HAS_USART2 +#error "USART2 not present in the selected device" +#endif + +#if STM32_UART_USE_USART3 && !STM32_HAS_USART3 +#error "USART3 not present in the selected device" +#endif + +#if STM32_UART_USE_UART4 && !STM32_HAS_UART4 +#error "UART4 not present in the selected device" +#endif + +#if STM32_UART_USE_UART5 && !STM32_HAS_UART5 +#error "UART5 not present in the selected device" +#endif + +#if STM32_UART_USE_UART7 && !STM32_HAS_UART7 +#error "UART7 not present in the selected device" +#endif + +#if STM32_UART_USE_UART8 && !STM32_HAS_UART8 +#error "UART8 not present in the selected device" +#endif + +#if !STM32_UART_USE_USART1 && !STM32_UART_USE_USART2 && \ + !STM32_UART_USE_USART3 && !STM32_UART_USE_UART4 && \ + !STM32_UART_USE_UART5 && !STM32_UART_USE_USART6 && \ + !STM32_UART_USE_UART7 && !STM32_UART_USE_UART8 +#error "UART driver activated but no USART/UART peripheral assigned" +#endif + +#if STM32_UART_USE_USART1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART1_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART1" +#endif + +#if STM32_UART_USE_USART2 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART2_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART2" +#endif + +#if STM32_UART_USE_USART3 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART3_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART3" +#endif + +#if STM32_UART_USE_UART4 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART4_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to UART4" +#endif + +#if STM32_UART_USE_UART5 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART5_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to UART5" +#endif + +#if STM32_UART_USE_USART6 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART6_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USART6" +#endif + +#if STM32_UART_USE_UART7 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART7_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to UART7" +#endif + +#if STM32_UART_USE_UART8 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART8_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to UART8" +#endif + +#if STM32_UART_USE_USART1 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART1_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART1" +#endif + +#if STM32_UART_USE_USART2 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART2_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART2" +#endif + +#if STM32_UART_USE_USART3 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART3_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART3" +#endif + +#if STM32_UART_USE_UART4 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART4_DMA_PRIORITY) +#error "Invalid DMA priority assigned to UART4" +#endif + +#if STM32_UART_USE_UART5 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART5_DMA_PRIORITY) +#error "Invalid DMA priority assigned to UART5" +#endif + +#if STM32_UART_USE_USART6 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART6_DMA_PRIORITY) +#error "Invalid DMA priority assigned to USART6" +#endif + +#if STM32_UART_USE_UART7 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART7_DMA_PRIORITY) +#error "Invalid DMA priority assigned to UART7" +#endif + +#if STM32_UART_USE_UART8 && \ + !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART8_DMA_PRIORITY) +#error "Invalid DMA priority assigned to UART8" +#endif + +/* The following checks are only required when there is a DMA able to + reassign streams to different channels.*/ +#if STM32_ADVANCED_DMA +/* Check on the presence of the DMA streams settings in mcuconf.h.*/ +#if STM32_UART_USE_USART1 && (!defined(STM32_UART_USART1_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART1_TX_DMA_STREAM)) +#error "USART1 DMA streams not defined" +#endif + +#if STM32_UART_USE_USART2 && (!defined(STM32_UART_USART2_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART2_TX_DMA_STREAM)) +#error "USART2 DMA streams not defined" +#endif + +#if STM32_UART_USE_USART3 && (!defined(STM32_UART_USART3_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART3_TX_DMA_STREAM)) +#error "USART3 DMA streams not defined" +#endif + +#if STM32_UART_USE_UART4 && (!defined(STM32_UART_UART4_RX_DMA_STREAM) || \ + !defined(STM32_UART_UART4_TX_DMA_STREAM)) +#error "UART4 DMA streams not defined" +#endif + +#if STM32_UART_USE_UART5 && (!defined(STM32_UART_UART5_RX_DMA_STREAM) || \ + !defined(STM32_UART_UART5_TX_DMA_STREAM)) +#error "UART5 DMA streams not defined" +#endif + +#if STM32_UART_USE_USART6 && (!defined(STM32_UART_USART6_RX_DMA_STREAM) || \ + !defined(STM32_UART_USART6_TX_DMA_STREAM)) +#error "USART6 DMA streams not defined" +#endif + +#if STM32_UART_USE_UART7 && (!defined(STM32_UART_UART7_RX_DMA_STREAM) || \ + !defined(STM32_UART_UART7_TX_DMA_STREAM)) +#error "UART7 DMA streams not defined" +#endif + +#if STM32_UART_USE_UART8 && (!defined(STM32_UART_UART8_RX_DMA_STREAM) || \ + !defined(STM32_UART_UART8_TX_DMA_STREAM)) +#error "UART8 DMA streams not defined" +#endif + +/* Check on the validity of the assigned DMA channels.*/ +#if STM32_UART_USE_USART1 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_RX_DMA_STREAM, \ + STM32_USART1_RX_DMA_MSK) +#error "invalid DMA stream associated to USART1 RX" +#endif + +#if STM32_UART_USE_USART1 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_TX_DMA_STREAM, \ + STM32_USART1_TX_DMA_MSK) +#error "invalid DMA stream associated to USART1 TX" +#endif + +#if STM32_UART_USE_USART2 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_RX_DMA_STREAM, \ + STM32_USART2_RX_DMA_MSK) +#error "invalid DMA stream associated to USART2 RX" +#endif + +#if STM32_UART_USE_USART2 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_TX_DMA_STREAM, \ + STM32_USART2_TX_DMA_MSK) +#error "invalid DMA stream associated to USART2 TX" +#endif + +#if STM32_UART_USE_USART3 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_RX_DMA_STREAM, \ + STM32_USART3_RX_DMA_MSK) +#error "invalid DMA stream associated to USART3 RX" +#endif + +#if STM32_UART_USE_USART3 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_TX_DMA_STREAM, \ + STM32_USART3_TX_DMA_MSK) +#error "invalid DMA stream associated to USART3 TX" +#endif + +#if STM32_UART_USE_UART4 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_RX_DMA_STREAM, \ + STM32_UART4_RX_DMA_MSK) +#error "invalid DMA stream associated to UART4 RX" +#endif + +#if STM32_UART_USE_UART4 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_TX_DMA_STREAM, \ + STM32_UART4_TX_DMA_MSK) +#error "invalid DMA stream associated to UART4 TX" +#endif + +#if STM32_UART_USE_UART5 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_RX_DMA_STREAM, \ + STM32_UART5_RX_DMA_MSK) +#error "invalid DMA stream associated to UART5 RX" +#endif + +#if STM32_UART_USE_UART5 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_TX_DMA_STREAM, \ + STM32_UART5_TX_DMA_MSK) +#error "invalid DMA stream associated to UART5 TX" +#endif + +#if STM32_UART_USE_USART6 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_RX_DMA_STREAM, \ + STM32_USART6_RX_DMA_MSK) +#error "invalid DMA stream associated to USART6 RX" +#endif + +#if STM32_UART_USE_USART6 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_TX_DMA_STREAM, \ + STM32_USART6_TX_DMA_MSK) +#error "invalid DMA stream associated to USART6 TX" +#endif + +#if STM32_UART_USE_UART7 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART7_RX_DMA_STREAM, \ + STM32_UART7_RX_DMA_MSK) +#error "invalid DMA stream associated to UART7 RX" +#endif + +#if STM32_UART_USE_UART7 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART7_TX_DMA_STREAM, \ + STM32_UART7_TX_DMA_MSK) +#error "invalid DMA stream associated to UART7 TX" +#endif + +#if STM32_UART_USE_UART8 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART8_RX_DMA_STREAM, \ + STM32_UART8_RX_DMA_MSK) +#error "invalid DMA stream associated to UART8 RX" +#endif + +#if STM32_UART_USE_UART8 && \ + !STM32_DMA_IS_VALID_ID(STM32_UART_UART8_TX_DMA_STREAM, \ + STM32_UART8_TX_DMA_MSK) +#error "invalid DMA stream associated to UART8 TX" +#endif +#endif /* STM32_ADVANCED_DMA */ + +#if !defined(STM32_DMA_REQUIRED) +#define STM32_DMA_REQUIRED +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief UART driver condition flags type. + */ +typedef uint32_t uartflags_t; + +/** + * @brief Structure representing an UART driver. + */ +typedef struct UARTDriver UARTDriver; + +/** + * @brief Generic UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + */ +typedef void (*uartcb_t)(UARTDriver *uartp); + +/** + * @brief Character received UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] c received character + */ +typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c); + +/** + * @brief Receive error UART notification callback type. + * + * @param[in] uartp pointer to the @p UARTDriver object + * @param[in] e receive error mask + */ +typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e); + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief End of transmission buffer callback. + */ + uartcb_t txend1_cb; + /** + * @brief Physical end of transmission callback. + */ + uartcb_t txend2_cb; + /** + * @brief Receive buffer filled callback. + */ + uartcb_t rxend_cb; + /** + * @brief Character received while out if the @p UART_RECEIVE state. + */ + uartccb_t rxchar_cb; + /** + * @brief Receive error callback. + */ + uartecb_t rxerr_cb; + /* End of the mandatory fields.*/ + /** + * @brief Bit rate. + */ + uint32_t speed; + /** + * @brief Initialization value for the CR1 register. + */ + uint32_t cr1; + /** + * @brief Initialization value for the CR2 register. + */ + uint32_t cr2; + /** + * @brief Initialization value for the CR3 register. + */ + uint32_t cr3; +} UARTConfig; + +/** + * @brief Structure representing an UART driver. + */ +struct UARTDriver { + /** + * @brief Driver state. + */ + uartstate_t state; + /** + * @brief Transmitter state. + */ + uarttxstate_t txstate; + /** + * @brief Receiver state. + */ + uartrxstate_t rxstate; + /** + * @brief Current configuration data. + */ + const UARTConfig *config; +#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Synchronization flag for transmit operations. + */ + bool early; + /** + * @brief Waiting thread on RX. + */ + thread_reference_t threadrx; + /** + * @brief Waiting thread on TX. + */ + thread_reference_t threadtx; +#endif /* UART_USE_WAIT */ +#if (UART_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) + /** + * @brief Mutex protecting the peripheral. + */ + mutex_t mutex; +#endif /* UART_USE_MUTUAL_EXCLUSION */ +#if defined(UART_DRIVER_EXT_FIELDS) + UART_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the USART registers block. + */ + USART_TypeDef *usart; + /** + * @brief Clock frequency for the associated USART/UART. + */ + uint32_t clock; + /** + * @brief DMA mode bit mask. + */ + uint32_t dmamode; + /** + * @brief Receive DMA channel. + */ + const stm32_dma_stream_t *dmarx; + /** + * @brief Transmit DMA channel. + */ + const stm32_dma_stream_t *dmatx; + /** + * @brief Default receive buffer while into @p UART_RX_IDLE state. + */ + volatile uint16_t rxbuf; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_UART_USE_USART1 && !defined(__DOXYGEN__) +extern UARTDriver UARTD1; +#endif + +#if STM32_UART_USE_USART2 && !defined(__DOXYGEN__) +extern UARTDriver UARTD2; +#endif + +#if STM32_UART_USE_USART3 && !defined(__DOXYGEN__) +extern UARTDriver UARTD3; +#endif + +#if STM32_UART_USE_UART4 && !defined(__DOXYGEN__) +extern UARTDriver UARTD4; +#endif + +#if STM32_UART_USE_UART5 && !defined(__DOXYGEN__) +extern UARTDriver UARTD5; +#endif + +#if STM32_UART_USE_USART6 && !defined(__DOXYGEN__) +extern UARTDriver UARTD6; +#endif + +#if STM32_UART_USE_UART7 && !defined(__DOXYGEN__) +extern UARTDriver UARTD7; +#endif + +#if STM32_UART_USE_UART8 && !defined(__DOXYGEN__) +extern UARTDriver UARTD8; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void uart_lld_init(void); + void uart_lld_start(UARTDriver *uartp); + void uart_lld_stop(UARTDriver *uartp); + void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf); + size_t uart_lld_stop_send(UARTDriver *uartp); + void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf); + size_t uart_lld_stop_receive(UARTDriver *uartp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_UART */ + +#endif /* _UART_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/serial_lld.c b/os/hal/ports/STM32/LLD/USARTv2/serial_lld.c deleted file mode 100644 index 6c9cf287d..000000000 --- a/os/hal/ports/STM32/LLD/USARTv2/serial_lld.c +++ /dev/null @@ -1,764 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv2/serial_lld.c - * @brief STM32 low level serial driver code. - * - * @addtogroup SERIAL - * @{ - */ - -#include "hal.h" - -#if HAL_USE_SERIAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/* For compatibility for those devices without LIN support in the USARTs.*/ -#if !defined(USART_ISR_LBDF) -#define USART_ISR_LBDF 0 -#endif - -#if !defined(USART_CR2_LBDIE) -#define USART_CR2_LBDIE 0 -#endif - -/* STM32L0xx/STM32F7xx ST headers difference.*/ -#if !defined(USART_ISR_LBDF) -#define USART_ISR_LBDF USART_ISR_LBD -#endif - -/* Handling the case where UART4 and UART5 are actually USARTs, this happens - in the STM32F0xx.*/ -#if defined(USART4) -#define UART4 USART4 -#endif - -#if defined(USART5) -#define UART5 USART5 -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief USART1 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) -SerialDriver SD1; -#endif - -/** @brief USART2 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) -SerialDriver SD2; -#endif - -/** @brief USART3 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) -SerialDriver SD3; -#endif - -/** @brief UART4 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) -SerialDriver SD4; -#endif - -/** @brief UART5 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) -SerialDriver SD5; -#endif - -/** @brief USART6 serial driver identifier.*/ -#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) -SerialDriver SD6; -#endif - -/** @brief UART7 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) -SerialDriver SD7; -#endif - -/** @brief UART8 serial driver identifier.*/ -#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -SerialDriver SD8; -#endif - -/** @brief LPUART1 serial driver identifier.*/ -#if STM32_SERIAL_USE_LPUART1 || defined(__DOXYGEN__) -SerialDriver LPSD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/** @brief Driver default configuration.*/ -static const SerialConfig default_config = -{ - SERIAL_DEFAULT_BITRATE, - 0, - USART_CR2_STOP1_BITS | USART_CR2_LINEN, - 0 -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief USART initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] config the architecture-dependent serial driver configuration - */ -static void usart_init(SerialDriver *sdp, const SerialConfig *config) { - USART_TypeDef *u = sdp->usart; - - /* Baud rate setting.*/ -#if STM32_SERIAL_USE_LPUART1 - if ( sdp == &LPSD1 ) - { - u->BRR = (uint32_t)( ( (uint64_t)sdp->clock * 256 ) / config->speed); - } - else -#endif - u->BRR = (uint32_t)(sdp->clock / config->speed); - - /* Note that some bits are enforced.*/ - u->CR2 = config->cr2 | USART_CR2_LBDIE; - u->CR3 = config->cr3 | USART_CR3_EIE; - u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE | - USART_CR1_RXNEIE | USART_CR1_TE | - USART_CR1_RE; - u->ICR = 0xFFFFFFFFU; -} - -/** - * @brief USART de-initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] u pointer to an USART I/O block - */ -static void usart_deinit(USART_TypeDef *u) { - - u->CR1 = 0; - u->CR2 = 0; - u->CR3 = 0; -} - -/** - * @brief Error handling routine. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] isr USART ISR register value - */ -static void set_error(SerialDriver *sdp, uint32_t isr) { - eventflags_t sts = 0; - - if (isr & USART_ISR_ORE) - sts |= SD_OVERRUN_ERROR; - if (isr & USART_ISR_PE) - sts |= SD_PARITY_ERROR; - if (isr & USART_ISR_FE) - sts |= SD_FRAMING_ERROR; - if (isr & USART_ISR_NE) - sts |= SD_NOISE_ERROR; - osalSysLockFromISR(); - chnAddFlagsI(sdp, sts); - osalSysUnlockFromISR(); -} - -/** - * @brief Common IRQ handler. - * - * @param[in] sdp communication channel associated to the USART - */ -static void serve_interrupt(SerialDriver *sdp) { - USART_TypeDef *u = sdp->usart; - uint32_t cr1 = u->CR1; - uint32_t isr; - - /* Reading and clearing status.*/ - isr = u->ISR; - u->ICR = isr; - - /* Error condition detection.*/ - if (isr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE | USART_ISR_PE)) - set_error(sdp, isr); - - /* Special case, LIN break detection.*/ - if (isr & USART_ISR_LBDF) { - osalSysLockFromISR(); - chnAddFlagsI(sdp, SD_BREAK_DETECTED); - osalSysUnlockFromISR(); - } - - /* Data available.*/ - if (isr & USART_ISR_RXNE) { - osalSysLockFromISR(); - sdIncomingDataI(sdp, (uint8_t)u->RDR); - osalSysUnlockFromISR(); - } - - /* Transmission buffer empty.*/ - if ((cr1 & USART_CR1_TXEIE) && (isr & USART_ISR_TXE)) { - msg_t b; - osalSysLockFromISR(); - b = oqGetI(&sdp->oqueue); - if (b < MSG_OK) { - chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY); - u->CR1 = (cr1 & ~USART_CR1_TXEIE) | USART_CR1_TCIE; - } - else - u->TDR = b; - osalSysUnlockFromISR(); - } - - /* Physical transmission end.*/ - if (isr & USART_ISR_TC) { - osalSysLockFromISR(); - if (oqIsEmptyI(&sdp->oqueue)) - chnAddFlagsI(sdp, CHN_TRANSMISSION_END); - u->CR1 = cr1 & ~USART_CR1_TCIE; - osalSysUnlockFromISR(); - } -} - -#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) -static void notify1(io_queue_t *qp) { - - (void)qp; - USART1->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) -static void notify2(io_queue_t *qp) { - - (void)qp; - USART2->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) -static void notify3(io_queue_t *qp) { - - (void)qp; - USART3->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) -static void notify4(io_queue_t *qp) { - - (void)qp; - UART4->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) -static void notify5(io_queue_t *qp) { - - (void)qp; - UART5->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) -static void notify6(io_queue_t *qp) { - - (void)qp; - USART6->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) -static void notify7(io_queue_t *qp) { - - (void)qp; - UART7->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -static void notify8(io_queue_t *qp) { - - (void)qp; - UART8->CR1 |= USART_CR1_TXEIE; -} -#endif - -#if STM32_SERIAL_USE_LPUART1 || defined(__DOXYGEN__) -static void notifylp1(io_queue_t *qp) { - - (void)qp; - LPUART1->CR1 |= USART_CR1_TXEIE; -} -#endif - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_SERIAL_USE_USART1 || defined(__DOXYGEN__) -#if !defined(STM32_USART1_HANDLER) -#error "STM32_USART1_HANDLER not defined" -#endif -/** - * @brief USART1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_USART2 || defined(__DOXYGEN__) -#if !defined(STM32_USART2_HANDLER) -#error "STM32_USART2_HANDLER not defined" -#endif -/** - * @brief USART2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if defined(STM32_USART3_8_HANDLER) -#if STM32_SERIAL_USE_USART3 || STM32_SERIAL_USE_UART4 || \ - STM32_SERIAL_USE_UART5 || STM32_SERIAL_USE_USART6 || \ - STM32_SERIAL_USE_UART7 || STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -/** - * @brief USART2 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART3_8_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - -#if STM32_SERIAL_USE_USART3 - serve_interrupt(&SD3); -#endif -#if STM32_SERIAL_USE_UART4 - serve_interrupt(&SD4); -#endif -#if STM32_SERIAL_USE_UART5 - serve_interrupt(&SD5); -#endif -#if STM32_SERIAL_USE_USART6 - serve_interrupt(&SD6); -#endif - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#else /* !defined(STM32_USART3_8_HANDLER) */ - -#if STM32_SERIAL_USE_USART3 || defined(__DOXYGEN__) -#if !defined(STM32_USART3_HANDLER) -#error "STM32_USART3_HANDLER not defined" -#endif -/** - * @brief USART3 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART4 || defined(__DOXYGEN__) -#if !defined(STM32_UART4_HANDLER) -#error "STM32_UART4_HANDLER not defined" -#endif -/** - * @brief UART4 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART5 || defined(__DOXYGEN__) -#if !defined(STM32_UART5_HANDLER) -#error "STM32_UART5_HANDLER not defined" -#endif -/** - * @brief UART5 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_USART6 || defined(__DOXYGEN__) -#if !defined(STM32_USART6_HANDLER) -#error "STM32_USART6_HANDLER not defined" -#endif -/** - * @brief USART6 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD6); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#endif /* !defined(STM32_USART3_8_HANDLER) */ - -#if STM32_SERIAL_USE_UART7 || defined(__DOXYGEN__) -#if !defined(STM32_UART7_HANDLER) -#error "STM32_UART7_HANDLER not defined" -#endif -/** - * @brief UART7 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART7_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD7); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -#if !defined(STM32_UART8_HANDLER) -#error "STM32_UART8_HANDLER not defined" -#endif -/** - * @brief UART8 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART8_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&SD8); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -#if STM32_SERIAL_USE_LPUART1 || defined(__DOXYGEN__) -#if !defined(STM32_LPUART1_HANDLER) -#error "STM32_LPUART1_HANDLER not defined" -#endif -/** - * @brief LPUART1 interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_LPUART1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_interrupt(&LPSD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level serial driver initialization. - * - * @notapi - */ -void sd_lld_init(void) { - -#if STM32_SERIAL_USE_USART1 - sdObjectInit(&SD1, NULL, notify1); - SD1.usart = USART1; - SD1.clock = STM32_USART1CLK; -#if defined(STM32_USART1_NUMBER) - nvicEnableVector(STM32_USART1_NUMBER, STM32_SERIAL_USART1_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_USART2 - sdObjectInit(&SD2, NULL, notify2); - SD2.usart = USART2; - SD2.clock = STM32_USART2CLK; -#if defined(STM32_USART2_NUMBER) - nvicEnableVector(STM32_USART2_NUMBER, STM32_SERIAL_USART2_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_USART3 - sdObjectInit(&SD3, NULL, notify3); - SD3.usart = USART3; - SD3.clock = STM32_USART3CLK; -#if defined(STM32_USART3_NUMBER) - nvicEnableVector(STM32_USART3_NUMBER, STM32_SERIAL_USART3_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_UART4 - sdObjectInit(&SD4, NULL, notify4); - SD4.usart = UART4; - SD4.clock = STM32_UART4CLK; -#if defined(STM32_UART4_NUMBER) - nvicEnableVector(STM32_UART4_NUMBER, STM32_SERIAL_UART4_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_UART5 - sdObjectInit(&SD5, NULL, notify5); - SD5.usart = UART5; - SD5.clock = STM32_UART5CLK; -#if defined(STM32_UART5_NUMBER) - nvicEnableVector(STM32_UART5_NUMBER, STM32_SERIAL_UART5_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_USART6 - sdObjectInit(&SD6, NULL, notify6); - SD6.usart = USART6; - SD6.clock = STM32_USART6CLK; -#if defined(STM32_USART6_NUMBER) - nvicEnableVector(STM32_USART6_NUMBER, STM32_SERIAL_USART6_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_UART7 - sdObjectInit(&SD7, NULL, notify7); - SD7.usart = UART7; - SD7.clock = STM32_UART7CLK; -#if defined(STM32_UART7_NUMBER) - nvicEnableVector(STM32_UART7_NUMBER, STM32_SERIAL_UART7_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_UART8 - sdObjectInit(&SD8, NULL, notify8); - SD8.usart = UART8; - SD8.clock = STM32_UART8CLK; -#if defined(STM32_UART8_NUMBER) - nvicEnableVector(STM32_UART8_NUMBER, STM32_SERIAL_UART8_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_LPUART1 - sdObjectInit(&LPSD1, NULL, notifylp1); - LPSD1.usart = LPUART1; - LPSD1.clock = STM32_LPUART1CLK; -#if defined(STM32_LPUART1_NUMBER) - nvicEnableVector(STM32_LPUART1_NUMBER, STM32_SERIAL_LPUART1_PRIORITY); -#endif -#endif - -#if STM32_SERIAL_USE_USART3 || STM32_SERIAL_USE_UART4 || \ - STM32_SERIAL_USE_UART5 || STM32_SERIAL_USE_USART6 || \ - STM32_SERIAL_USE_UART7 || STM32_SERIAL_USE_UART8 || defined(__DOXYGEN__) -#if defined(STM32_USART3_8_HANDLER) - nvicEnableVector(STM32_USART3_8_NUMBER, STM32_SERIAL_USART3_8_PRIORITY); -#endif -#endif -} - -/** - * @brief Low level serial driver configuration and (re)start. - * - * @param[in] sdp pointer to a @p SerialDriver object - * @param[in] config the architecture-dependent serial driver configuration. - * If this parameter is set to @p NULL then a default - * configuration is used. - * - * @notapi - */ -void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) { - - if (config == NULL) - config = &default_config; - - if (sdp->state == SD_STOP) { -#if STM32_SERIAL_USE_USART1 - if (&SD1 == sdp) { - rccEnableUSART1(FALSE); - } -#endif -#if STM32_SERIAL_USE_USART2 - if (&SD2 == sdp) { - rccEnableUSART2(FALSE); - } -#endif -#if STM32_SERIAL_USE_USART3 - if (&SD3 == sdp) { - rccEnableUSART3(FALSE); - } -#endif -#if STM32_SERIAL_USE_UART4 - if (&SD4 == sdp) { - rccEnableUART4(FALSE); - } -#endif -#if STM32_SERIAL_USE_UART5 - if (&SD5 == sdp) { - rccEnableUART5(FALSE); - } -#endif -#if STM32_SERIAL_USE_USART6 - if (&SD6 == sdp) { - rccEnableUSART6(FALSE); - } -#endif -#if STM32_SERIAL_USE_UART7 - if (&SD7 == sdp) { - rccEnableUART7(FALSE); - } -#endif -#if STM32_SERIAL_USE_UART8 - if (&SD8 == sdp) { - rccEnableUART8(FALSE); - } -#endif -#if STM32_SERIAL_USE_LPUART1 - if (&LPSD1 == sdp) { - rccEnableLPUART1(FALSE); - } -#endif - } - usart_init(sdp, config); -} - -/** - * @brief Low level serial driver stop. - * @details De-initializes the USART, stops the associated clock, resets the - * interrupt vector. - * - * @param[in] sdp pointer to a @p SerialDriver object - * - * @notapi - */ -void sd_lld_stop(SerialDriver *sdp) { - - if (sdp->state == SD_READY) { - /* UART is de-initialized then clocks are disabled.*/ - usart_deinit(sdp->usart); - -#if STM32_SERIAL_USE_USART1 - if (&SD1 == sdp) { - rccDisableUSART1(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_USART2 - if (&SD2 == sdp) { - rccDisableUSART2(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_USART3 - if (&SD3 == sdp) { - rccDisableUSART3(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_UART4 - if (&SD4 == sdp) { - rccDisableUART4(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_UART5 - if (&SD5 == sdp) { - rccDisableUART5(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_USART6 - if (&SD6 == sdp) { - rccDisableUSART6(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_UART7 - if (&SD7 == sdp) { - rccDisableUART7(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_UART8 - if (&SD8 == sdp) { - rccDisableUART8(FALSE); - return; - } -#endif -#if STM32_SERIAL_USE_LPUART1 - if (&LPSD1 == sdp) { - rccDisableLPUART1(FALSE); - return; - } -#endif - } -} - -#endif /* HAL_USE_SERIAL */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/serial_lld.h b/os/hal/ports/STM32/LLD/USARTv2/serial_lld.h deleted file mode 100644 index 478395030..000000000 --- a/os/hal/ports/STM32/LLD/USARTv2/serial_lld.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv2/serial_lld.h - * @brief STM32 low level serial driver header. - * - * @addtogroup SERIAL - * @{ - */ - -#ifndef _SERIAL_LLD_H_ -#define _SERIAL_LLD_H_ - -#if HAL_USE_SERIAL || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief USART1 driver enable switch. - * @details If set to @p TRUE the support for USART1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART1) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART1 FALSE -#endif - -/** - * @brief USART2 driver enable switch. - * @details If set to @p TRUE the support for USART2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART2) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART2 FALSE -#endif - -/** - * @brief USART3 driver enable switch. - * @details If set to @p TRUE the support for USART3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART3) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART3 FALSE -#endif - -/** - * @brief UART4 driver enable switch. - * @details If set to @p TRUE the support for UART4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART4) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART4 FALSE -#endif - -/** - * @brief UART5 driver enable switch. - * @details If set to @p TRUE the support for UART5 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART5) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART5 FALSE -#endif - -/** - * @brief USART6 driver enable switch. - * @details If set to @p TRUE the support for USART6 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_USART6) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_USART6 FALSE -#endif - -/** - * @brief UART7 driver enable switch. - * @details If set to @p TRUE the support for UART7 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART7) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART7 FALSE -#endif - -/** - * @brief UART8 driver enable switch. - * @details If set to @p TRUE the support for UART8 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_UART8) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_UART8 FALSE -#endif - -/** - * @brief LPUART1 driver enable switch. - * @details If set to @p TRUE the support for LPUART is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_SERIAL_USE_LPUART1) || defined(__DOXYGEN__) -#define STM32_SERIAL_USE_LPUART1 FALSE -#endif - -/** - * @brief USART1 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART1_PRIORITY 12 -#endif - -/** - * @brief USART2 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART2_PRIORITY 12 -#endif - -/** - * @brief USART3 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART3_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART3_PRIORITY 12 -#endif - -/** - * @brief USART3..8 interrupt priority level setting. - * @note Only valid on those devices with a shared IRQ. - */ -#if !defined(STM32_SERIAL_USART3_8_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART3_8_PRIORITY 12 -#endif - -/** - * @brief UART4 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART4_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART4_PRIORITY 12 -#endif - -/** - * @brief UART5 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART5_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART5_PRIORITY 12 -#endif - -/** - * @brief USART6 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_USART6_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_USART6_PRIORITY 12 -#endif - -/** - * @brief UART7 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART7_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART7_PRIORITY 12 -#endif - -/** - * @brief UART8 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_UART8_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_UART8_PRIORITY 12 -#endif - -/** - * @brief LPUART1 interrupt priority level setting. - */ -#if !defined(STM32_SERIAL_LPUART1_PRIORITY) || defined(__DOXYGEN__) -#define STM32_SERIAL_LPUART1_PRIORITY 12 -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_SERIAL_USE_USART1 && !STM32_HAS_USART1 -#error "USART1 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_USART2 && !STM32_HAS_USART2 -#error "USART2 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_USART3 && !STM32_HAS_USART3 -#error "USART3 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART4 && !STM32_HAS_UART4 -#error "UART4 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART5 && !STM32_HAS_UART5 -#error "UART5 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_USART6 && !STM32_HAS_USART6 -#error "USART6 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART7 && !STM32_HAS_UART7 -#error "UART7 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_UART8 && !STM32_HAS_UART8 -#error "UART8 not present in the selected device" -#endif - -#if STM32_SERIAL_USE_LPUART1 && !STM32_HAS_LPUART1 -#error "LPUART1 not present in the selected device" -#endif - -#if !STM32_SERIAL_USE_USART1 && !STM32_SERIAL_USE_USART2 && \ - !STM32_SERIAL_USE_USART3 && !STM32_SERIAL_USE_UART4 && \ - !STM32_SERIAL_USE_UART5 && !STM32_SERIAL_USE_USART6 && \ - !STM32_SERIAL_USE_UART7 && !STM32_SERIAL_USE_UART8 && \ - !STM32_SERIAL_USE_LPUART1 -#error "SERIAL driver activated but no USART/UART peripheral assigned" -#endif - -#if STM32_SERIAL_USE_USART1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART1_PRIORITY) -#error "Invalid IRQ priority assigned to USART1" -#endif - -#if STM32_SERIAL_USE_USART2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART2_PRIORITY) -#error "Invalid IRQ priority assigned to USART2" -#endif - -#if STM32_SERIAL_USE_USART3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART3_PRIORITY) -#error "Invalid IRQ priority assigned to USART3" -#endif - -#if STM32_SERIAL_USE_UART4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART4_PRIORITY) -#error "Invalid IRQ priority assigned to UART4" -#endif - -#if STM32_SERIAL_USE_UART5 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART5_PRIORITY) -#error "Invalid IRQ priority assigned to UART5" -#endif - -#if STM32_SERIAL_USE_USART6 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_USART6_PRIORITY) -#error "Invalid IRQ priority assigned to USART6" -#endif - -#if STM32_SERIAL_USE_UART7 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART7_PRIORITY) -#error "Invalid IRQ priority assigned to UART7" -#endif - -#if STM32_SERIAL_USE_UART8 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_UART8_PRIORITY) -#error "Invalid IRQ priority assigned to UART8" -#endif - -#if STM32_SERIAL_USE_LPUART1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_SERIAL_LPUART1_PRIORITY) -#error "Invalid IRQ priority assigned to LPUART1" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief STM32 Serial Driver configuration structure. - * @details An instance of this structure must be passed to @p sdStart() - * in order to configure and start a serial driver operations. - * @note This structure content is architecture dependent, each driver - * implementation defines its own version and the custom static - * initializers. - */ -typedef struct { - /** - * @brief Bit rate. - */ - uint32_t speed; - /* End of the mandatory fields.*/ - /** - * @brief Initialization value for the CR1 register. - */ - uint32_t cr1; - /** - * @brief Initialization value for the CR2 register. - */ - uint32_t cr2; - /** - * @brief Initialization value for the CR3 register. - */ - uint32_t cr3; -} SerialConfig; - -/** - * @brief @p SerialDriver specific data. - */ -#define _serial_driver_data \ - _base_asynchronous_channel_data \ - /* Driver state.*/ \ - sdstate_t state; \ - /* Input queue.*/ \ - input_queue_t iqueue; \ - /* Output queue.*/ \ - output_queue_t oqueue; \ - /* Input circular buffer.*/ \ - uint8_t ib[SERIAL_BUFFERS_SIZE]; \ - /* Output circular buffer.*/ \ - uint8_t ob[SERIAL_BUFFERS_SIZE]; \ - /* End of the mandatory fields.*/ \ - /* Pointer to the USART registers block.*/ \ - USART_TypeDef *usart; \ - /* Clock frequency for the associated USART/UART.*/ \ - uint32_t clock; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/* - * Extra USARTs definitions here (missing from the ST header file). - */ -#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/ -#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/ -#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/ -#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_SERIAL_USE_USART1 && !defined(__DOXYGEN__) -extern SerialDriver SD1; -#endif -#if STM32_SERIAL_USE_USART2 && !defined(__DOXYGEN__) -extern SerialDriver SD2; -#endif -#if STM32_SERIAL_USE_USART3 && !defined(__DOXYGEN__) -extern SerialDriver SD3; -#endif -#if STM32_SERIAL_USE_UART4 && !defined(__DOXYGEN__) -extern SerialDriver SD4; -#endif -#if STM32_SERIAL_USE_UART5 && !defined(__DOXYGEN__) -extern SerialDriver SD5; -#endif -#if STM32_SERIAL_USE_USART6 && !defined(__DOXYGEN__) -extern SerialDriver SD6; -#endif -#if STM32_SERIAL_USE_UART7 && !defined(__DOXYGEN__) -extern SerialDriver SD7; -#endif -#if STM32_SERIAL_USE_UART8 && !defined(__DOXYGEN__) -extern SerialDriver SD8; -#endif -#if STM32_SERIAL_USE_LPUART1 && !defined(__DOXYGEN__) -extern SerialDriver LPSD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void sd_lld_init(void); - void sd_lld_start(SerialDriver *sdp, const SerialConfig *config); - void sd_lld_stop(SerialDriver *sdp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_SERIAL */ - -#endif /* _SERIAL_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c b/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c deleted file mode 100644 index f72effb67..000000000 --- a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.c +++ /dev/null @@ -1,931 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv2/uart_lld.c - * @brief STM32 low level UART driver code. - * - * @addtogroup UART - * @{ - */ - -#include "hal.h" - -#if HAL_USE_UART || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -/* STM32L0xx/STM32F7xx ST headers difference.*/ -#if !defined(USART_ISR_LBDF) -#define USART_ISR_LBDF USART_ISR_LBD -#endif - -#define USART1_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART1_RX_DMA_STREAM, \ - STM32_USART1_RX_DMA_CHN) - -#define USART1_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART1_TX_DMA_STREAM, \ - STM32_USART1_TX_DMA_CHN) - -#define USART2_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART2_RX_DMA_STREAM, \ - STM32_USART2_RX_DMA_CHN) - -#define USART2_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART2_TX_DMA_STREAM, \ - STM32_USART2_TX_DMA_CHN) - -#define USART3_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART3_RX_DMA_STREAM, \ - STM32_USART3_RX_DMA_CHN) - -#define USART3_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART3_TX_DMA_STREAM, \ - STM32_USART3_TX_DMA_CHN) - -#define UART4_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART4_RX_DMA_STREAM, \ - STM32_UART4_RX_DMA_CHN) - -#define UART4_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART4_TX_DMA_STREAM, \ - STM32_UART4_TX_DMA_CHN) - -#define UART5_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART5_RX_DMA_STREAM, \ - STM32_UART5_RX_DMA_CHN) - -#define UART5_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART5_TX_DMA_STREAM, \ - STM32_UART5_TX_DMA_CHN) - -#define USART6_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART6_RX_DMA_STREAM, \ - STM32_USART6_RX_DMA_CHN) - -#define USART6_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_USART6_TX_DMA_STREAM, \ - STM32_USART6_TX_DMA_CHN) - -#define UART7_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART7_RX_DMA_STREAM, \ - STM32_UART7_RX_DMA_CHN) - -#define UART7_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART7_TX_DMA_STREAM, \ - STM32_UART7_TX_DMA_CHN) - -#define UART8_RX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART8_RX_DMA_STREAM, \ - STM32_UART8_RX_DMA_CHN) - -#define UART8_TX_DMA_CHANNEL \ - STM32_DMA_GETCHANNEL(STM32_UART_UART8_TX_DMA_STREAM, \ - STM32_UART8_TX_DMA_CHN) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief USART1 UART driver identifier.*/ -#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) -UARTDriver UARTD1; -#endif - -/** @brief USART2 UART driver identifier.*/ -#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) -UARTDriver UARTD2; -#endif - -/** @brief USART3 UART driver identifier.*/ -#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) -UARTDriver UARTD3; -#endif - -/** @brief UART4 UART driver identifier.*/ -#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) -UARTDriver UARTD4; -#endif - -/** @brief UART5 UART driver identifier.*/ -#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) -UARTDriver UARTD5; -#endif - -/** @brief USART6 UART driver identifier.*/ -#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) -UARTDriver UARTD6; -#endif - -/** @brief UART7 UART driver identifier.*/ -#if STM32_UART_USE_UART7 || defined(__DOXYGEN__) -UARTDriver UARTD7; -#endif - -/** @brief UART8 UART driver identifier.*/ -#if STM32_UART_USE_UART8 || defined(__DOXYGEN__) -UARTDriver UARTD8; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Status bits translation. - * - * @param[in] sr USART SR register value - * - * @return The error flags. - */ -static uartflags_t translate_errors(uint32_t isr) { - uartflags_t sts = 0; - - if (isr & USART_ISR_ORE) - sts |= UART_OVERRUN_ERROR; - if (isr & USART_ISR_PE) - sts |= UART_PARITY_ERROR; - if (isr & USART_ISR_FE) - sts |= UART_FRAMING_ERROR; - if (isr & USART_ISR_NE) - sts |= UART_NOISE_ERROR; - if (isr & USART_ISR_LBDF) - sts |= UART_BREAK_DETECTED; - return sts; -} - -/** - * @brief Puts the receiver in the UART_RX_IDLE state. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void uart_enter_rx_idle_loop(UARTDriver *uartp) { - uint32_t mode; - - /* RX DMA channel preparation, if the char callback is defined then the - TCIE interrupt is enabled too.*/ - if (uartp->config->rxchar_cb == NULL) - mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC; - else - mode = STM32_DMA_CR_DIR_P2M | STM32_DMA_CR_CIRC | STM32_DMA_CR_TCIE; - dmaStreamSetMemory0(uartp->dmarx, &uartp->rxbuf); - dmaStreamSetTransactionSize(uartp->dmarx, 1); - dmaStreamSetMode(uartp->dmarx, uartp->dmamode | mode); - dmaStreamEnable(uartp->dmarx); -} - -/** - * @brief USART de-initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void usart_stop(UARTDriver *uartp) { - - /* Stops RX and TX DMA channels.*/ - dmaStreamDisable(uartp->dmarx); - dmaStreamDisable(uartp->dmatx); - - /* Stops USART operations.*/ - uartp->usart->CR1 = 0; - uartp->usart->CR2 = 0; - uartp->usart->CR3 = 0; -} - -/** - * @brief USART initialization. - * @details This function must be invoked with interrupts disabled. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void usart_start(UARTDriver *uartp) { - uint32_t cr1; - USART_TypeDef *u = uartp->usart; - - /* Defensive programming, starting from a clean state.*/ - usart_stop(uartp); - - /* Baud rate setting.*/ - u->BRR = (uint32_t)(uartp->clock / uartp->config->speed); - - /* Resetting eventual pending status flags.*/ - u->ICR = 0xFFFFFFFFU; - - /* Note that some bits are enforced because required for correct driver - operations.*/ - u->CR2 = uartp->config->cr2 | USART_CR2_LBDIE; - u->CR3 = uartp->config->cr3 | USART_CR3_DMAT | USART_CR3_DMAR | - USART_CR3_EIE; - - /* Mustn't ever set TCIE here - if done, it causes an immediate - interrupt.*/ - cr1 = USART_CR1_UE | USART_CR1_PEIE | USART_CR1_TE | USART_CR1_RE; - u->CR1 = uartp->config->cr1 | cr1; - - /* Starting the receiver idle loop.*/ - uart_enter_rx_idle_loop(uartp); -} - -/** - * @brief RX DMA common service routine. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void uart_lld_serve_rx_end_irq(UARTDriver *uartp, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_UART_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_UART_DMA_ERROR_HOOK(uartp); - } -#else - (void)flags; -#endif - - if (uartp->rxstate == UART_RX_IDLE) { - /* Receiver in idle state, a callback is generated, if enabled, for each - received character and then the driver stays in the same state.*/ - _uart_rx_idle_code(uartp); - } - else { - /* Receiver in active state, a callback is generated, if enabled, after - a completed transfer.*/ - dmaStreamDisable(uartp->dmarx); - _uart_rx_complete_isr_code(uartp); - } -} - -/** - * @brief TX DMA common service routine. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] flags pre-shifted content of the ISR register - */ -static void uart_lld_serve_tx_end_irq(UARTDriver *uartp, uint32_t flags) { - - /* DMA errors handling.*/ -#if defined(STM32_UART_DMA_ERROR_HOOK) - if ((flags & (STM32_DMA_ISR_TEIF | STM32_DMA_ISR_DMEIF)) != 0) { - STM32_UART_DMA_ERROR_HOOK(uartp); - } -#else - (void)flags; -#endif - - dmaStreamDisable(uartp->dmatx); - - /* A callback is generated, if enabled, after a completed transfer.*/ - _uart_tx1_isr_code(uartp); -} - -/** - * @brief USART common service routine. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -static void serve_usart_irq(UARTDriver *uartp) { - uint32_t isr; - USART_TypeDef *u = uartp->usart; - uint32_t cr1 = u->CR1; - - /* Reading and clearing status.*/ - isr = u->ISR; - u->ICR = isr; - - if (isr & (USART_ISR_LBDF | USART_ISR_ORE | USART_ISR_NE | - USART_ISR_FE | USART_ISR_PE)) { - _uart_rx_error_isr_code(uartp, translate_errors(isr)); - } - - if ((isr & USART_ISR_TC) && (cr1 & USART_CR1_TCIE)) { - /* TC interrupt disabled.*/ - u->CR1 = cr1 & ~USART_CR1_TCIE; - - /* End of transmission, a callback is generated.*/ - _uart_tx2_isr_code(uartp); - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_UART_USE_USART1 || defined(__DOXYGEN__) -#if !defined(STM32_USART1_HANDLER) -#error "STM32_USART1_HANDLER not defined" -#endif -/** - * @brief USART1 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART1_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD1); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART1 */ - -#if STM32_UART_USE_USART2 || defined(__DOXYGEN__) -#if !defined(STM32_USART2_HANDLER) -#error "STM32_USART2_HANDLER not defined" -#endif -/** - * @brief USART2 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART2_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD2); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART2 */ - -#if STM32_UART_USE_USART3 || defined(__DOXYGEN__) -#if !defined(STM32_USART3_HANDLER) -#error "STM32_USART3_HANDLER not defined" -#endif -/** - * @brief USART3 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART3_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD3); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART3 */ - -#if STM32_UART_USE_UART4 || defined(__DOXYGEN__) -#if !defined(STM32_UART4_HANDLER) -#error "STM32_UART4_HANDLER not defined" -#endif -/** - * @brief UART4 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART4_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD4); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_UART4 */ - -#if STM32_UART_USE_UART5 || defined(__DOXYGEN__) -#if !defined(STM32_UART5_HANDLER) -#error "STM32_UART5_HANDLER not defined" -#endif -/** - * @brief UART5 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART5_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD5); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_UART5 */ - -#if STM32_UART_USE_USART6 || defined(__DOXYGEN__) -#if !defined(STM32_USART6_HANDLER) -#error "STM32_USART6_HANDLER not defined" -#endif -/** - * @brief USART6 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USART6_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD6); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_USART6 */ - -#if STM32_UART_USE_UART7 || defined(__DOXYGEN__) -#if !defined(STM32_UART7_HANDLER) -#error "STM32_UART7_HANDLER not defined" -#endif -/** - * @brief UART7 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART7_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD7); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_UART7 */ - -#if STM32_UART_USE_UART8 || defined(__DOXYGEN__) -#if !defined(STM32_UART8_HANDLER) -#error "STM32_UART8_HANDLER not defined" -#endif -/** - * @brief UART8 IRQ handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_UART8_HANDLER) { - - OSAL_IRQ_PROLOGUE(); - - serve_usart_irq(&UARTD8); - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_UART_USE_UART8 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level UART driver initialization. - * - * @notapi - */ -void uart_lld_init(void) { - -#if STM32_UART_USE_USART1 - uartObjectInit(&UARTD1); - UARTD1.usart = USART1; - UARTD1.clock = STM32_USART1CLK; - UARTD1.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD1.dmarx = STM32_DMA_STREAM(STM32_UART_USART1_RX_DMA_STREAM); - UARTD1.dmatx = STM32_DMA_STREAM(STM32_UART_USART1_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_USART2 - uartObjectInit(&UARTD2); - UARTD2.usart = USART2; - UARTD2.clock = STM32_USART2CLK; - UARTD2.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD2.dmarx = STM32_DMA_STREAM(STM32_UART_USART2_RX_DMA_STREAM); - UARTD2.dmatx = STM32_DMA_STREAM(STM32_UART_USART2_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_USART3 - uartObjectInit(&UARTD3); - UARTD3.usart = USART3; - UARTD3.clock = STM32_USART3CLK; - UARTD3.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD3.dmarx = STM32_DMA_STREAM(STM32_UART_USART3_RX_DMA_STREAM); - UARTD3.dmatx = STM32_DMA_STREAM(STM32_UART_USART3_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_UART4 - uartObjectInit(&UARTD4); - UARTD4.usart = UART4; - UARTD4.clock = STM32_UART4CLK; - UARTD4.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD4.dmarx = STM32_DMA_STREAM(STM32_UART_UART4_RX_DMA_STREAM); - UARTD4.dmatx = STM32_DMA_STREAM(STM32_UART_UART4_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_UART5 - uartObjectInit(&UARTD5); - UARTD5.usart = UART5; - UARTD5.clock = STM32_UART5CLK; - UARTD5.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD5.dmarx = STM32_DMA_STREAM(STM32_UART_UART5_RX_DMA_STREAM); - UARTD5.dmatx = STM32_DMA_STREAM(STM32_UART_UART5_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_USART6 - uartObjectInit(&UARTD6); - UARTD6.usart = USART6; - UARTD6.clock = STM32_USART6CLK; - UARTD6.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD6.dmarx = STM32_DMA_STREAM(STM32_UART_USART6_RX_DMA_STREAM); - UARTD6.dmatx = STM32_DMA_STREAM(STM32_UART_USART6_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_UART7 - uartObjectInit(&UARTD7); - UARTD7.usart = UART7; - UARTD7.clock = STM32_UART7CLK; - UARTD7.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD7.dmarx = STM32_DMA_STREAM(STM32_UART_UART7_RX_DMA_STREAM); - UARTD7.dmatx = STM32_DMA_STREAM(STM32_UART_UART7_TX_DMA_STREAM); -#endif - -#if STM32_UART_USE_UART8 - uartObjectInit(&UARTD8); - UARTD8.usart = UART8; - UARTD8.clock = STM32_UART8CLK; - UARTD8.dmamode = STM32_DMA_CR_DMEIE | STM32_DMA_CR_TEIE; - UARTD8.dmarx = STM32_DMA_STREAM(STM32_UART_UART8_RX_DMA_STREAM); - UARTD8.dmatx = STM32_DMA_STREAM(STM32_UART_UART8_TX_DMA_STREAM); -#endif -} - -/** - * @brief Configures and activates the UART peripheral. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @notapi - */ -void uart_lld_start(UARTDriver *uartp) { - - if (uartp->state == UART_STOP) { -#if STM32_UART_USE_USART1 - if (&UARTD1 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART1_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART1_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART1(FALSE); - nvicEnableVector(STM32_USART1_NUMBER, STM32_UART_USART1_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART1_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART1_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_USART2 - if (&UARTD2 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART2_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART2_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART2(FALSE); - nvicEnableVector(STM32_USART2_NUMBER, STM32_UART_USART2_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART2_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART2_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_USART3 - if (&UARTD3 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART3_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART3_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART3(FALSE); - nvicEnableVector(STM32_USART3_NUMBER, STM32_UART_USART3_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART3_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART3_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_UART4 - if (&UARTD4 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_UART4_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_UART4_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUART4(FALSE); - nvicEnableVector(STM32_UART4_NUMBER, STM32_UART_UART4_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(UART4_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_UART4_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_UART5 - if (&UARTD5 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_UART5_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_UART5_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUART5(FALSE); - nvicEnableVector(STM32_UART5_NUMBER, STM32_UART_UART5_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(UART5_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_UART5_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_USART6 - if (&UARTD6 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_USART6_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_USART6_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUSART6(FALSE); - nvicEnableVector(STM32_USART6_NUMBER, STM32_UART_USART6_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(USART6_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_USART6_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_UART7 - if (&UARTD7 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_UART7_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_UART7_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUART7(FALSE); - nvicEnableVector(STM32_UART7_NUMBER, STM32_UART_UART7_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(UART7_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_UART7_DMA_PRIORITY); - } -#endif - -#if STM32_UART_USE_UART8 - if (&UARTD8 == uartp) { - bool b; - b = dmaStreamAllocate(uartp->dmarx, - STM32_UART_UART8_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_rx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - b = dmaStreamAllocate(uartp->dmatx, - STM32_UART_UART8_IRQ_PRIORITY, - (stm32_dmaisr_t)uart_lld_serve_tx_end_irq, - (void *)uartp); - osalDbgAssert(!b, "stream already allocated"); - rccEnableUART8(FALSE); - nvicEnableVector(STM32_UART8_NUMBER, STM32_UART_UART8_IRQ_PRIORITY); - uartp->dmamode |= STM32_DMA_CR_CHSEL(UART8_RX_DMA_CHANNEL) | - STM32_DMA_CR_PL(STM32_UART_UART8_DMA_PRIORITY); - } -#endif - - /* Static DMA setup, the transfer size depends on the USART settings, - it is 16 bits if M=1 and PCE=0 else it is 8 bits.*/ - if ((uartp->config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_M) - uartp->dmamode |= STM32_DMA_CR_PSIZE_HWORD | STM32_DMA_CR_MSIZE_HWORD; - dmaStreamSetPeripheral(uartp->dmarx, &uartp->usart->RDR); - dmaStreamSetPeripheral(uartp->dmatx, &uartp->usart->TDR); - uartp->rxbuf = 0; - } - - uartp->rxstate = UART_RX_IDLE; - uartp->txstate = UART_TX_IDLE; - usart_start(uartp); -} - -/** - * @brief Deactivates the UART peripheral. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @notapi - */ -void uart_lld_stop(UARTDriver *uartp) { - - if (uartp->state == UART_READY) { - usart_stop(uartp); - dmaStreamRelease(uartp->dmarx); - dmaStreamRelease(uartp->dmatx); - -#if STM32_UART_USE_USART1 - if (&UARTD1 == uartp) { - nvicDisableVector(STM32_USART1_NUMBER); - rccDisableUSART1(FALSE); - return; - } -#endif - -#if STM32_UART_USE_USART2 - if (&UARTD2 == uartp) { - nvicDisableVector(STM32_USART2_NUMBER); - rccDisableUSART2(FALSE); - return; - } -#endif - -#if STM32_UART_USE_USART3 - if (&UARTD3 == uartp) { - nvicDisableVector(STM32_USART3_NUMBER); - rccDisableUSART3(FALSE); - return; - } -#endif - -#if STM32_UART_USE_UART4 - if (&UARTD4 == uartp) { - nvicDisableVector(STM32_UART4_NUMBER); - rccDisableUART4(FALSE); - return; - } -#endif - -#if STM32_UART_USE_UART5 - if (&UARTD5 == uartp) { - nvicDisableVector(STM32_UART5_NUMBER); - rccDisableUART5(FALSE); - return; - } -#endif - -#if STM32_UART_USE_USART6 - if (&UARTD6 == uartp) { - nvicDisableVector(STM32_USART6_NUMBER); - rccDisableUSART6(FALSE); - return; - } -#endif - -#if STM32_UART_USE_UART7 - if (&UARTD7 == uartp) { - nvicDisableVector(STM32_UART7_NUMBER); - rccDisableUART7(FALSE); - return; - } -#endif - -#if STM32_UART_USE_UART8 - if (&UARTD8 == uartp) { - nvicDisableVector(STM32_UART8_NUMBER); - rccDisableUART8(FALSE); - return; - } -#endif - } -} - -/** - * @brief Starts a transmission on the UART peripheral. - * @note The buffers are organized as uint8_t arrays for data sizes below - * or equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] n number of data frames to send - * @param[in] txbuf the pointer to the transmit buffer - * - * @notapi - */ -void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf) { - - /* TX DMA channel preparation.*/ - dmaStreamSetMemory0(uartp->dmatx, txbuf); - dmaStreamSetTransactionSize(uartp->dmatx, n); - dmaStreamSetMode(uartp->dmatx, uartp->dmamode | STM32_DMA_CR_DIR_M2P | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); - - /* Only enable TC interrupt if there's a callback attached to it. - Also we need to clear TC flag which could be set before. */ - if (uartp->config->txend2_cb != NULL) { - uartp->usart->ICR = USART_ICR_TCCF; - uartp->usart->CR1 |= USART_CR1_TCIE; - } - - /* Starting transfer.*/ - dmaStreamEnable(uartp->dmatx); -} - -/** - * @brief Stops any ongoing transmission. - * @note Stopping a transmission also suppresses the transmission callbacks. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @return The number of data frames not transmitted by the - * stopped transmit operation. - * - * @notapi - */ -size_t uart_lld_stop_send(UARTDriver *uartp) { - - dmaStreamDisable(uartp->dmatx); - - return dmaStreamGetTransactionSize(uartp->dmatx); -} - -/** - * @brief Starts a receive operation on the UART peripheral. - * @note The buffers are organized as uint8_t arrays for data sizes below - * or equal to 8 bits else it is organized as uint16_t arrays. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] n number of data frames to send - * @param[out] rxbuf the pointer to the receive buffer - * - * @notapi - */ -void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf) { - - /* Stopping previous activity (idle state).*/ - dmaStreamDisable(uartp->dmarx); - - /* RX DMA channel preparation.*/ - dmaStreamSetMemory0(uartp->dmarx, rxbuf); - dmaStreamSetTransactionSize(uartp->dmarx, n); - dmaStreamSetMode(uartp->dmarx, uartp->dmamode | STM32_DMA_CR_DIR_P2M | - STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE); - - /* Starting transfer.*/ - dmaStreamEnable(uartp->dmarx); -} - -/** - * @brief Stops any ongoing receive operation. - * @note Stopping a receive operation also suppresses the receive callbacks. - * - * @param[in] uartp pointer to the @p UARTDriver object - * - * @return The number of data frames not received by the - * stopped receive operation. - * - * @notapi - */ -size_t uart_lld_stop_receive(UARTDriver *uartp) { - size_t n; - - dmaStreamDisable(uartp->dmarx); - n = dmaStreamGetTransactionSize(uartp->dmarx); - uart_enter_rx_idle_loop(uartp); - - return n; -} - -#endif /* HAL_USE_UART */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h b/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h deleted file mode 100644 index 3b3c8ce00..000000000 --- a/os/hal/ports/STM32/LLD/USARTv2/uart_lld.h +++ /dev/null @@ -1,734 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USARTv2/uart_lld.h - * @brief STM32 low level UART driver header. - * - * @addtogroup UART - * @{ - */ - -#ifndef _UART_LLD_H_ -#define _UART_LLD_H_ - -#if HAL_USE_UART || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief UART driver on USART1 enable switch. - * @details If set to @p TRUE the support for USART1 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART1) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART1 FALSE -#endif - -/** - * @brief UART driver on USART2 enable switch. - * @details If set to @p TRUE the support for USART2 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART2) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART2 FALSE -#endif - -/** - * @brief UART driver on USART3 enable switch. - * @details If set to @p TRUE the support for USART3 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART3) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART3 FALSE -#endif - -/** - * @brief UART driver on UART4 enable switch. - * @details If set to @p TRUE the support for UART4 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_UART4) || defined(__DOXYGEN__) -#define STM32_UART_USE_UART4 FALSE -#endif - -/** - * @brief UART driver on UART5 enable switch. - * @details If set to @p TRUE the support for UART5 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_UART5) || defined(__DOXYGEN__) -#define STM32_UART_USE_UART5 FALSE -#endif - -/** - * @brief UART driver on USART6 enable switch. - * @details If set to @p TRUE the support for USART6 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_USART6) || defined(__DOXYGEN__) -#define STM32_UART_USE_USART6 FALSE -#endif - -/** - * @brief UART driver on UART7 enable switch. - * @details If set to @p TRUE the support for UART7 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_UART7) || defined(__DOXYGEN__) -#define STM32_UART_USE_UART7 FALSE -#endif - -/** - * @brief UART driver on UART8 enable switch. - * @details If set to @p TRUE the support for UART8 is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_UART_USE_UART8) || defined(__DOXYGEN__) -#define STM32_UART_USE_UART8 FALSE -#endif - -/** - * @brief USART1 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART1_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART1_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART2 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART2_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART2_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART3 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART3_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART3_IRQ_PRIORITY 12 -#endif - -/** - * @brief UART4 interrupt priority level setting. - */ -#if !defined(STM32_UART_UART4_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART4_IRQ_PRIORITY 12 -#endif - -/** - * @brief UART5 interrupt priority level setting. - */ -#if !defined(STM32_UART_UART5_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART5_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART6 interrupt priority level setting. - */ -#if !defined(STM32_UART_USART6_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART6_IRQ_PRIORITY 12 -#endif - -/** - * @brief UART7 interrupt priority level setting. - */ -#if !defined(STM32_UART_UART7_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART7_IRQ_PRIORITY 12 -#endif - -/** - * @brief UART8 interrupt priority level setting. - */ -#if !defined(STM32_UART_UART8_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART8_IRQ_PRIORITY 12 -#endif - -/** - * @brief USART1 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART1_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART1_DMA_PRIORITY 0 -#endif - -/** - * @brief USART2 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART2_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART2_DMA_PRIORITY 0 -#endif - -/** - * @brief USART3 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART3_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART3_DMA_PRIORITY 0 -#endif - -/** - * @brief UART4 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_UART4_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART4_DMA_PRIORITY 0 -#endif - -/** - * @brief UART5 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_UART5_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART5_DMA_PRIORITY 0 -#endif - -/** - * @brief USART6 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_USART6_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_USART6_DMA_PRIORITY 0 -#endif - -/** - * @brief UART7 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_UART7_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART7_DMA_PRIORITY 0 -#endif - -/** - * @brief UART8 DMA priority (0..3|lowest..highest). - * @note The priority level is used for both the TX and RX DMA channels but - * because of the channels ordering the RX channel has always priority - * over the TX channel. - */ -#if !defined(STM32_UART_UART8_DMA_PRIORITY) || defined(__DOXYGEN__) -#define STM32_UART_UART8_DMA_PRIORITY 0 -#endif - -/** - * @brief UART DMA error hook. - * @note The default action for DMA errors is a system halt because DMA - * error can only happen because programming errors. - */ -#if !defined(STM32_UART_DMA_ERROR_HOOK) || defined(__DOXYGEN__) -#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure") -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_UART_USE_USART1 && !STM32_HAS_USART1 -#error "USART1 not present in the selected device" -#endif - -#if STM32_UART_USE_USART2 && !STM32_HAS_USART2 -#error "USART2 not present in the selected device" -#endif - -#if STM32_UART_USE_USART3 && !STM32_HAS_USART3 -#error "USART3 not present in the selected device" -#endif - -#if STM32_UART_USE_UART4 && !STM32_HAS_UART4 -#error "UART4 not present in the selected device" -#endif - -#if STM32_UART_USE_UART5 && !STM32_HAS_UART5 -#error "UART5 not present in the selected device" -#endif - -#if STM32_UART_USE_UART7 && !STM32_HAS_UART7 -#error "UART7 not present in the selected device" -#endif - -#if STM32_UART_USE_UART8 && !STM32_HAS_UART8 -#error "UART8 not present in the selected device" -#endif - -#if !STM32_UART_USE_USART1 && !STM32_UART_USE_USART2 && \ - !STM32_UART_USE_USART3 && !STM32_UART_USE_UART4 && \ - !STM32_UART_USE_UART5 && !STM32_UART_USE_USART6 && \ - !STM32_UART_USE_UART7 && !STM32_UART_USE_UART8 -#error "UART driver activated but no USART/UART peripheral assigned" -#endif - -#if STM32_UART_USE_USART1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART1_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART1" -#endif - -#if STM32_UART_USE_USART2 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART2_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART2" -#endif - -#if STM32_UART_USE_USART3 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART3_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART3" -#endif - -#if STM32_UART_USE_UART4 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART4_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to UART4" -#endif - -#if STM32_UART_USE_UART5 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART5_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to UART5" -#endif - -#if STM32_UART_USE_USART6 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_USART6_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USART6" -#endif - -#if STM32_UART_USE_UART7 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART7_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to UART7" -#endif - -#if STM32_UART_USE_UART8 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_UART_UART8_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to UART8" -#endif - -#if STM32_UART_USE_USART1 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART1_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART1" -#endif - -#if STM32_UART_USE_USART2 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART2_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART2" -#endif - -#if STM32_UART_USE_USART3 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART3_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART3" -#endif - -#if STM32_UART_USE_UART4 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART4_DMA_PRIORITY) -#error "Invalid DMA priority assigned to UART4" -#endif - -#if STM32_UART_USE_UART5 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART5_DMA_PRIORITY) -#error "Invalid DMA priority assigned to UART5" -#endif - -#if STM32_UART_USE_USART6 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_USART6_DMA_PRIORITY) -#error "Invalid DMA priority assigned to USART6" -#endif - -#if STM32_UART_USE_UART7 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART7_DMA_PRIORITY) -#error "Invalid DMA priority assigned to UART7" -#endif - -#if STM32_UART_USE_UART8 && \ - !STM32_DMA_IS_VALID_PRIORITY(STM32_UART_UART8_DMA_PRIORITY) -#error "Invalid DMA priority assigned to UART8" -#endif - -/* The following checks are only required when there is a DMA able to - reassign streams to different channels.*/ -#if STM32_ADVANCED_DMA -/* Check on the presence of the DMA streams settings in mcuconf.h.*/ -#if STM32_UART_USE_USART1 && (!defined(STM32_UART_USART1_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART1_TX_DMA_STREAM)) -#error "USART1 DMA streams not defined" -#endif - -#if STM32_UART_USE_USART2 && (!defined(STM32_UART_USART2_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART2_TX_DMA_STREAM)) -#error "USART2 DMA streams not defined" -#endif - -#if STM32_UART_USE_USART3 && (!defined(STM32_UART_USART3_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART3_TX_DMA_STREAM)) -#error "USART3 DMA streams not defined" -#endif - -#if STM32_UART_USE_UART4 && (!defined(STM32_UART_UART4_RX_DMA_STREAM) || \ - !defined(STM32_UART_UART4_TX_DMA_STREAM)) -#error "UART4 DMA streams not defined" -#endif - -#if STM32_UART_USE_UART5 && (!defined(STM32_UART_UART5_RX_DMA_STREAM) || \ - !defined(STM32_UART_UART5_TX_DMA_STREAM)) -#error "UART5 DMA streams not defined" -#endif - -#if STM32_UART_USE_USART6 && (!defined(STM32_UART_USART6_RX_DMA_STREAM) || \ - !defined(STM32_UART_USART6_TX_DMA_STREAM)) -#error "USART6 DMA streams not defined" -#endif - -#if STM32_UART_USE_UART7 && (!defined(STM32_UART_UART7_RX_DMA_STREAM) || \ - !defined(STM32_UART_UART7_TX_DMA_STREAM)) -#error "UART7 DMA streams not defined" -#endif - -#if STM32_UART_USE_UART8 && (!defined(STM32_UART_UART8_RX_DMA_STREAM) || \ - !defined(STM32_UART_UART8_TX_DMA_STREAM)) -#error "UART8 DMA streams not defined" -#endif - -/* Check on the validity of the assigned DMA channels.*/ -#if STM32_UART_USE_USART1 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_RX_DMA_STREAM, \ - STM32_USART1_RX_DMA_MSK) -#error "invalid DMA stream associated to USART1 RX" -#endif - -#if STM32_UART_USE_USART1 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART1_TX_DMA_STREAM, \ - STM32_USART1_TX_DMA_MSK) -#error "invalid DMA stream associated to USART1 TX" -#endif - -#if STM32_UART_USE_USART2 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_RX_DMA_STREAM, \ - STM32_USART2_RX_DMA_MSK) -#error "invalid DMA stream associated to USART2 RX" -#endif - -#if STM32_UART_USE_USART2 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART2_TX_DMA_STREAM, \ - STM32_USART2_TX_DMA_MSK) -#error "invalid DMA stream associated to USART2 TX" -#endif - -#if STM32_UART_USE_USART3 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_RX_DMA_STREAM, \ - STM32_USART3_RX_DMA_MSK) -#error "invalid DMA stream associated to USART3 RX" -#endif - -#if STM32_UART_USE_USART3 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART3_TX_DMA_STREAM, \ - STM32_USART3_TX_DMA_MSK) -#error "invalid DMA stream associated to USART3 TX" -#endif - -#if STM32_UART_USE_UART4 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_RX_DMA_STREAM, \ - STM32_UART4_RX_DMA_MSK) -#error "invalid DMA stream associated to UART4 RX" -#endif - -#if STM32_UART_USE_UART4 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART4_TX_DMA_STREAM, \ - STM32_UART4_TX_DMA_MSK) -#error "invalid DMA stream associated to UART4 TX" -#endif - -#if STM32_UART_USE_UART5 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_RX_DMA_STREAM, \ - STM32_UART5_RX_DMA_MSK) -#error "invalid DMA stream associated to UART5 RX" -#endif - -#if STM32_UART_USE_UART5 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART5_TX_DMA_STREAM, \ - STM32_UART5_TX_DMA_MSK) -#error "invalid DMA stream associated to UART5 TX" -#endif - -#if STM32_UART_USE_USART6 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_RX_DMA_STREAM, \ - STM32_USART6_RX_DMA_MSK) -#error "invalid DMA stream associated to USART6 RX" -#endif - -#if STM32_UART_USE_USART6 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_USART6_TX_DMA_STREAM, \ - STM32_USART6_TX_DMA_MSK) -#error "invalid DMA stream associated to USART6 TX" -#endif - -#if STM32_UART_USE_UART7 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART7_RX_DMA_STREAM, \ - STM32_UART7_RX_DMA_MSK) -#error "invalid DMA stream associated to UART7 RX" -#endif - -#if STM32_UART_USE_UART7 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART7_TX_DMA_STREAM, \ - STM32_UART7_TX_DMA_MSK) -#error "invalid DMA stream associated to UART7 TX" -#endif - -#if STM32_UART_USE_UART8 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART8_RX_DMA_STREAM, \ - STM32_UART8_RX_DMA_MSK) -#error "invalid DMA stream associated to UART8 RX" -#endif - -#if STM32_UART_USE_UART8 && \ - !STM32_DMA_IS_VALID_ID(STM32_UART_UART8_TX_DMA_STREAM, \ - STM32_UART8_TX_DMA_MSK) -#error "invalid DMA stream associated to UART8 TX" -#endif -#endif /* STM32_ADVANCED_DMA */ - -#if !defined(STM32_DMA_REQUIRED) -#define STM32_DMA_REQUIRED -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief UART driver condition flags type. - */ -typedef uint32_t uartflags_t; - -/** - * @brief Structure representing an UART driver. - */ -typedef struct UARTDriver UARTDriver; - -/** - * @brief Generic UART notification callback type. - * - * @param[in] uartp pointer to the @p UARTDriver object - */ -typedef void (*uartcb_t)(UARTDriver *uartp); - -/** - * @brief Character received UART notification callback type. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] c received character - */ -typedef void (*uartccb_t)(UARTDriver *uartp, uint16_t c); - -/** - * @brief Receive error UART notification callback type. - * - * @param[in] uartp pointer to the @p UARTDriver object - * @param[in] e receive error mask - */ -typedef void (*uartecb_t)(UARTDriver *uartp, uartflags_t e); - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief End of transmission buffer callback. - */ - uartcb_t txend1_cb; - /** - * @brief Physical end of transmission callback. - */ - uartcb_t txend2_cb; - /** - * @brief Receive buffer filled callback. - */ - uartcb_t rxend_cb; - /** - * @brief Character received while out if the @p UART_RECEIVE state. - */ - uartccb_t rxchar_cb; - /** - * @brief Receive error callback. - */ - uartecb_t rxerr_cb; - /* End of the mandatory fields.*/ - /** - * @brief Bit rate. - */ - uint32_t speed; - /** - * @brief Initialization value for the CR1 register. - */ - uint32_t cr1; - /** - * @brief Initialization value for the CR2 register. - */ - uint32_t cr2; - /** - * @brief Initialization value for the CR3 register. - */ - uint32_t cr3; -} UARTConfig; - -/** - * @brief Structure representing an UART driver. - */ -struct UARTDriver { - /** - * @brief Driver state. - */ - uartstate_t state; - /** - * @brief Transmitter state. - */ - uarttxstate_t txstate; - /** - * @brief Receiver state. - */ - uartrxstate_t rxstate; - /** - * @brief Current configuration data. - */ - const UARTConfig *config; -#if (UART_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Synchronization flag for transmit operations. - */ - bool early; - /** - * @brief Waiting thread on RX. - */ - thread_reference_t threadrx; - /** - * @brief Waiting thread on TX. - */ - thread_reference_t threadtx; -#endif /* UART_USE_WAIT */ -#if (UART_USE_MUTUAL_EXCLUSION == TRUE) || defined(__DOXYGEN__) - /** - * @brief Mutex protecting the peripheral. - */ - mutex_t mutex; -#endif /* UART_USE_MUTUAL_EXCLUSION */ -#if defined(UART_DRIVER_EXT_FIELDS) - UART_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the USART registers block. - */ - USART_TypeDef *usart; - /** - * @brief Clock frequency for the associated USART/UART. - */ - uint32_t clock; - /** - * @brief DMA mode bit mask. - */ - uint32_t dmamode; - /** - * @brief Receive DMA channel. - */ - const stm32_dma_stream_t *dmarx; - /** - * @brief Transmit DMA channel. - */ - const stm32_dma_stream_t *dmatx; - /** - * @brief Default receive buffer while into @p UART_RX_IDLE state. - */ - volatile uint16_t rxbuf; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_UART_USE_USART1 && !defined(__DOXYGEN__) -extern UARTDriver UARTD1; -#endif - -#if STM32_UART_USE_USART2 && !defined(__DOXYGEN__) -extern UARTDriver UARTD2; -#endif - -#if STM32_UART_USE_USART3 && !defined(__DOXYGEN__) -extern UARTDriver UARTD3; -#endif - -#if STM32_UART_USE_UART4 && !defined(__DOXYGEN__) -extern UARTDriver UARTD4; -#endif - -#if STM32_UART_USE_UART5 && !defined(__DOXYGEN__) -extern UARTDriver UARTD5; -#endif - -#if STM32_UART_USE_USART6 && !defined(__DOXYGEN__) -extern UARTDriver UARTD6; -#endif - -#if STM32_UART_USE_UART7 && !defined(__DOXYGEN__) -extern UARTDriver UARTD7; -#endif - -#if STM32_UART_USE_UART8 && !defined(__DOXYGEN__) -extern UARTDriver UARTD8; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void uart_lld_init(void); - void uart_lld_start(UARTDriver *uartp); - void uart_lld_stop(UARTDriver *uartp); - void uart_lld_start_send(UARTDriver *uartp, size_t n, const void *txbuf); - size_t uart_lld_stop_send(UARTDriver *uartp); - void uart_lld_start_receive(UARTDriver *uartp, size_t n, void *rxbuf); - size_t uart_lld_stop_receive(UARTDriver *uartp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_UART */ - -#endif /* _UART_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.c b/os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.c new file mode 100644 index 000000000..20bddbc94 --- /dev/null +++ b/os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.c @@ -0,0 +1,863 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USBv1/usb_lld.c + * @brief STM32 USB subsystem low level driver source. + * + * @addtogroup USB + * @{ + */ + +#include + +#include "hal.h" + +#if HAL_USE_USB || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define BTABLE_ADDR 0x0000 + +#define EPR_EP_TYPE_IS_ISO(bits) ((bits & EPR_EP_TYPE_MASK) == EPR_EP_TYPE_ISO) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** @brief USB1 driver identifier.*/ +#if STM32_USB_USE_USB1 || defined(__DOXYGEN__) +USBDriver USBD1; +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +/** + * @brief EP0 state. + * @note It is an union because IN and OUT endpoints are never used at the + * same time for EP0. + */ +static union { + /** + * @brief IN EP0 state. + */ + USBInEndpointState in; + /** + * @brief OUT EP0 state. + */ + USBOutEndpointState out; +} ep0_state; + +/** + * @brief Buffer for the EP0 setup packets. + */ +static uint8_t ep0setup_buffer[8]; + +/** + * @brief EP0 initialization structure. + */ +static const USBEndpointConfig ep0config = { + USB_EP_MODE_TYPE_CTRL, + _usb_ep0setup, + _usb_ep0in, + _usb_ep0out, + 0x40, + 0x40, + &ep0_state.in, + &ep0_state.out, + 1, + ep0setup_buffer +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Resets the packet memory allocator. + * + * @param[in] usbp pointer to the @p USBDriver object + */ +static void usb_pm_reset(USBDriver *usbp) { + + /* The first 64 bytes are reserved for the descriptors table. The effective + available RAM for endpoint buffers is just 448 bytes.*/ + usbp->pmnext = 64; +} + +/** + * @brief Resets the packet memory allocator. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] size size of the packet buffer to allocate + */ +static uint32_t usb_pm_alloc(USBDriver *usbp, size_t size) { + uint32_t next; + + next = usbp->pmnext; + usbp->pmnext += (size + 1) & ~1; + osalDbgAssert(usbp->pmnext <= STM32_USB_PMA_SIZE, "PMA overflow"); + return next; +} + +/** + * @brief Reads from a dedicated packet buffer. + * + * @param[in] udp pointer to a @p stm32_usb_descriptor_t + * @param[out] buf buffer where to copy the packet data + * @return The size of the receivee packet. + * + * @notapi + */ +static size_t usb_packet_read_to_buffer(usbep_t ep, uint8_t *buf) { + size_t i, n; + stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); + stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->RXADDR0); +#if STM32_USB_USE_ISOCHRONOUS + uint32_t epr = STM32_USB->EPR[ep]; + + /* Double buffering is always enabled for isochronous endpoints, and + although we overlap the two buffers for simplicity, we still need + to read from the right counter. The DTOG_RX bit indicates the buffer + that is currently in use by the USB peripheral, that is, the buffer + in which the next received packet will be stored, so we need to + read the counter of the OTHER buffer, which is where the last + received packet was stored.*/ + if (EPR_EP_TYPE_IS_ISO(epr) && !(epr & EPR_DTOG_RX)) + n = (size_t)udp->RXCOUNT1 & RXCOUNT_COUNT_MASK; + else + n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; +#else + n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; +#endif + + i = n; + +#if STM32_USB_USE_FAST_COPY + while (i >= 16) { + uint32_t w; + + w = *(pmap + 0); + *(buf + 0) = (uint8_t)w; + *(buf + 1) = (uint8_t)(w >> 8); + w = *(pmap + 1); + *(buf + 2) = (uint8_t)w; + *(buf + 3) = (uint8_t)(w >> 8); + w = *(pmap + 2); + *(buf + 4) = (uint8_t)w; + *(buf + 5) = (uint8_t)(w >> 8); + w = *(pmap + 3); + *(buf + 6) = (uint8_t)w; + *(buf + 7) = (uint8_t)(w >> 8); + w = *(pmap + 4); + *(buf + 8) = (uint8_t)w; + *(buf + 9) = (uint8_t)(w >> 8); + w = *(pmap + 5); + *(buf + 10) = (uint8_t)w; + *(buf + 11) = (uint8_t)(w >> 8); + w = *(pmap + 6); + *(buf + 12) = (uint8_t)w; + *(buf + 13) = (uint8_t)(w >> 8); + w = *(pmap + 7); + *(buf + 14) = (uint8_t)w; + *(buf + 15) = (uint8_t)(w >> 8); + + i -= 16; + buf += 16; + pmap += 8; + } +#endif /* STM32_USB_USE_FAST_COPY */ + + while (i >= 2) { + uint32_t w = *pmap++; + *buf++ = (uint8_t)w; + *buf++ = (uint8_t)(w >> 8); + i -= 2; + } + + if (i >= 1) { + *buf = (uint8_t)*pmap; + } + + return n; +} + +/** + * @brief Writes to a dedicated packet buffer. + * + * @param[in] ep endpoint number + * @param[in] buf buffer where to fetch the packet data + * @param[in] n maximum number of bytes to copy. This value must + * not exceed the maximum packet size for this endpoint. + * + * @notapi + */ +static void usb_packet_write_from_buffer(usbep_t ep, + const uint8_t *buf, + size_t n) { + stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); + stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->TXADDR0); + int i = (int)n; + +#if STM32_USB_USE_ISOCHRONOUS + uint32_t epr = STM32_USB->EPR[ep]; + + /* Double buffering is always enabled for isochronous endpoints, and + although we overlap the two buffers for simplicity, we still need + to write to the right counter. The DTOG_TX bit indicates the buffer + that is currently in use by the USB peripheral, that is, the buffer + from which the next packet will be sent, so we need to write the + counter of that buffer.*/ + if (EPR_EP_TYPE_IS_ISO(epr) && (epr & EPR_DTOG_TX)) + udp->TXCOUNT1 = (stm32_usb_pma_t)n; + else + udp->TXCOUNT0 = (stm32_usb_pma_t)n; +#else + udp->TXCOUNT0 = (stm32_usb_pma_t)n; +#endif + +#if STM32_USB_USE_FAST_COPY + while (i >= 16) { + uint32_t w; + + w = *(buf + 0); + w |= *(buf + 1) << 8; + *(pmap + 0) = (stm32_usb_pma_t)w; + w = *(buf + 2); + w |= *(buf + 3) << 8; + *(pmap + 1) = (stm32_usb_pma_t)w; + w = *(buf + 4); + w |= *(buf + 5) << 8; + *(pmap + 2) = (stm32_usb_pma_t)w; + w = *(buf + 6); + w |= *(buf + 7) << 8; + *(pmap + 3) = (stm32_usb_pma_t)w; + w = *(buf + 8); + w |= *(buf + 9) << 8; + *(pmap + 4) = (stm32_usb_pma_t)w; + w = *(buf + 10); + w |= *(buf + 11) << 8; + *(pmap + 5) = (stm32_usb_pma_t)w; + w = *(buf + 12); + w |= *(buf + 13) << 8; + *(pmap + 6) = (stm32_usb_pma_t)w; + w = *(buf + 14); + w |= *(buf + 15) << 8; + *(pmap + 7) = (stm32_usb_pma_t)w; + + i -= 16; + buf += 16; + pmap += 8; + } +#endif /* STM32_USB_USE_FAST_COPY */ + + while (i > 0) { + uint32_t w; + + w = *buf++; + w |= *buf++ << 8; + *pmap++ = (stm32_usb_pma_t)w; + i -= 2; + } +} + +/** + * @brief Common ISR code, serves the EP-related interrupts. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +static void usb_serve_endpoints(USBDriver *usbp, uint32_t ep) { + size_t n; + uint32_t epr = STM32_USB->EPR[ep]; + const USBEndpointConfig *epcp = usbp->epc[ep]; + + if (epr & EPR_CTR_TX) { + /* IN endpoint, transmission.*/ + USBInEndpointState *isp = epcp->in_state; + + EPR_CLEAR_CTR_TX(ep); + + isp->txcnt += isp->txlast; + n = isp->txsize - isp->txcnt; + if (n > 0) { + /* Transfer not completed, there are more packets to send.*/ + if (n > epcp->in_maxsize) + n = epcp->in_maxsize; + + /* Writes the packet from the defined buffer.*/ + isp->txbuf += isp->txlast; + isp->txlast = n; + usb_packet_write_from_buffer(ep, isp->txbuf, n); + + /* Starting IN operation.*/ + EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); + } + else { + /* Transfer completed, invokes the callback.*/ + _usb_isr_invoke_in_cb(usbp, ep); + } + } + if (epr & EPR_CTR_RX) { + /* OUT endpoint, receive.*/ + + EPR_CLEAR_CTR_RX(ep); + + if (epr & EPR_SETUP) { + /* Setup packets handling, setup packets are handled using a + specific callback.*/ + _usb_isr_invoke_setup_cb(usbp, ep); + } + else { + USBOutEndpointState *osp = epcp->out_state; + + /* Reads the packet into the defined buffer.*/ + n = usb_packet_read_to_buffer(ep, osp->rxbuf); + osp->rxbuf += n; + + /* Transaction data updated.*/ + osp->rxcnt += n; + osp->rxsize -= n; + osp->rxpkts -= 1; + + /* The transaction is completed if the specified number of packets + has been received or the current packet is a short packet.*/ + if ((n < epcp->out_maxsize) || (osp->rxpkts == 0)) { + /* Transfer complete, invokes the callback.*/ + _usb_isr_invoke_out_cb(usbp, ep); + } + else { + /* Transfer not complete, there are more packets to receive.*/ + EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID); + } + } + } +} + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +#if STM32_USB_USE_USB1 || defined(__DOXYGEN__) +#if STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER +#if STM32_USB_USE_ISOCHRONOUS +/** + * @brief USB high priority interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USB1_HP_HANDLER) { + uint32_t istr; + USBDriver *usbp = &USBD1; + + OSAL_IRQ_PROLOGUE(); + + /* Endpoint events handling.*/ + istr = STM32_USB->ISTR; + while (istr & ISTR_CTR) { + usb_serve_endpoints(usbp, istr & ISTR_EP_ID_MASK); + istr = STM32_USB->ISTR; + } + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_USB_USE_ISOCHRONOUS */ +#endif /* STM32_USB1_LP_NUMBER != STM32_USB1_HP_NUMBER */ + +/** + * @brief USB low priority interrupt handler. + * + * @isr + */ +OSAL_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { + uint32_t istr; + USBDriver *usbp = &USBD1; + + OSAL_IRQ_PROLOGUE(); + + istr = STM32_USB->ISTR; + + /* USB bus reset condition handling.*/ + if (istr & ISTR_RESET) { + STM32_USB->ISTR = ~ISTR_RESET; + + _usb_reset(usbp); + } + + /* USB bus SUSPEND condition handling.*/ + if (istr & ISTR_SUSP) { + STM32_USB->CNTR |= CNTR_FSUSP; +#if STM32_USB_LOW_POWER_ON_SUSPEND + STM32_USB->CNTR |= CNTR_LP_MODE; +#endif + STM32_USB->ISTR = ~ISTR_SUSP; + + _usb_suspend(usbp); + } + + /* USB bus WAKEUP condition handling.*/ + if (istr & ISTR_WKUP) { + uint32_t fnr = STM32_USB->FNR; + if (!(fnr & FNR_RXDP)) { + STM32_USB->CNTR &= ~CNTR_FSUSP; + + _usb_wakeup(usbp); + } +#if STM32_USB_LOW_POWER_ON_SUSPEND + else { + /* Just noise, going back in SUSPEND mode, reference manual 22.4.5, + table 169.*/ + STM32_USB->CNTR |= CNTR_LP_MODE; + } +#endif + STM32_USB->ISTR = ~ISTR_WKUP; + } + + /* SOF handling.*/ + if (istr & ISTR_SOF) { + _usb_isr_invoke_sof_cb(usbp); + STM32_USB->ISTR = ~ISTR_SOF; + } + + /* Endpoint events handling.*/ + while (istr & ISTR_CTR) { + usb_serve_endpoints(usbp, istr & ISTR_EP_ID_MASK); + istr = STM32_USB->ISTR; + } + + OSAL_IRQ_EPILOGUE(); +} +#endif /* STM32_USB_USE_USB1 */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level USB driver initialization. + * + * @notapi + */ +void usb_lld_init(void) { + + /* Driver initialization.*/ + usbObjectInit(&USBD1); +} + +/** + * @brief Configures and activates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_start(USBDriver *usbp) { + + if (usbp->state == USB_STOP) { + /* Clock activation.*/ +#if STM32_USB_USE_USB1 + if (&USBD1 == usbp) { + /* USB clock enabled.*/ + rccEnableUSB(FALSE); + /* Powers up the transceiver while holding the USB in reset state.*/ + STM32_USB->CNTR = CNTR_FRES; + /* Enabling the USB IRQ vectors, this also gives enough time to allow + the transceiver power up (1uS).*/ +#if STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER + nvicEnableVector(STM32_USB1_HP_NUMBER, STM32_USB_USB1_HP_IRQ_PRIORITY); +#endif + nvicEnableVector(STM32_USB1_LP_NUMBER, STM32_USB_USB1_LP_IRQ_PRIORITY); + /* Releases the USB reset.*/ + STM32_USB->CNTR = 0; + } +#endif + /* Reset procedure enforced on driver start.*/ + _usb_reset(usbp); + } +} + +/** + * @brief Deactivates the USB peripheral. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_stop(USBDriver *usbp) { + + /* If in ready state then disables the USB clock.*/ + if (usbp->state == USB_STOP) { +#if STM32_USB_USE_USB1 + if (&USBD1 == usbp) { +#if STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER + nvicDisableVector(STM32_USB1_HP_NUMBER); +#endif + nvicDisableVector(STM32_USB1_LP_NUMBER); + STM32_USB->CNTR = CNTR_PDWN | CNTR_FRES; + rccDisableUSB(FALSE); + } +#endif + } +} + +/** + * @brief USB low level reset routine. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_reset(USBDriver *usbp) { + uint32_t cntr; + + /* Post reset initialization.*/ + STM32_USB->BTABLE = BTABLE_ADDR; + STM32_USB->ISTR = 0; + STM32_USB->DADDR = DADDR_EF; + cntr = /*CNTR_ESOFM | */ CNTR_RESETM | CNTR_SUSPM | + CNTR_WKUPM | /*CNTR_ERRM | CNTR_PMAOVRM |*/ CNTR_CTRM; + /* The SOF interrupt is only enabled if a callback is defined for + this service because it is an high rate source.*/ + if (usbp->config->sof_cb != NULL) + cntr |= CNTR_SOFM; + STM32_USB->CNTR = cntr; + + /* Resets the packet memory allocator.*/ + usb_pm_reset(usbp); + + /* EP0 initialization.*/ + usbp->epc[0] = &ep0config; + usb_lld_init_endpoint(usbp, 0); +} + +/** + * @brief Sets the USB address. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_set_address(USBDriver *usbp) { + + STM32_USB->DADDR = (uint32_t)(usbp->address) | DADDR_EF; +} + +/** + * @brief Enables an endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { + uint16_t epr; + stm32_usb_descriptor_t *dp; + const USBEndpointConfig *epcp = usbp->epc[ep]; + + /* Setting the endpoint type. Note that isochronous endpoints cannot be + bidirectional because it uses double buffering and both transmit and + receive descriptor fields are used for either direction.*/ + switch (epcp->ep_mode & USB_EP_MODE_TYPE) { + case USB_EP_MODE_TYPE_ISOC: +#if STM32_USB_USE_ISOCHRONOUS + osalDbgAssert((epcp->in_state == NULL) || (epcp->out_state == NULL), + "isochronous EP cannot be IN and OUT"); + epr = EPR_EP_TYPE_ISO; + break; +#else + osalDbgAssert(false, "isochronous support disabled"); +#endif + case USB_EP_MODE_TYPE_BULK: + epr = EPR_EP_TYPE_BULK; + break; + case USB_EP_MODE_TYPE_INTR: + epr = EPR_EP_TYPE_INTERRUPT; + break; + default: + epr = EPR_EP_TYPE_CONTROL; + } + + dp = USB_GET_DESCRIPTOR(ep); + + /* IN endpoint handling.*/ + if (epcp->in_state != NULL) { + dp->TXCOUNT0 = 0; + dp->TXADDR0 = usb_pm_alloc(usbp, epcp->in_maxsize); + +#if STM32_USB_USE_ISOCHRONOUS + if (epr == EPR_EP_TYPE_ISO) { + epr |= EPR_STAT_TX_VALID; + dp->TXCOUNT1 = dp->TXCOUNT0; + dp->TXADDR1 = dp->TXADDR0; /* Both buffers overlapped.*/ + } + else { + epr |= EPR_STAT_TX_NAK; + } +#else + epr |= EPR_STAT_TX_NAK; +#endif + } + + /* OUT endpoint handling.*/ + if (epcp->out_state != NULL) { + uint16_t nblocks; + + /* Endpoint size and address initialization.*/ + if (epcp->out_maxsize > 62) + nblocks = (((((epcp->out_maxsize - 1) | 0x1f) + 1) / 32) << 10) | + 0x8000; + else + nblocks = ((((epcp->out_maxsize - 1) | 1) + 1) / 2) << 10; + dp->RXCOUNT0 = nblocks; + dp->RXADDR0 = usb_pm_alloc(usbp, epcp->out_maxsize); + +#if STM32_USB_USE_ISOCHRONOUS + if (epr == EPR_EP_TYPE_ISO) { + epr |= EPR_STAT_RX_VALID; + dp->RXCOUNT1 = dp->RXCOUNT0; + dp->RXADDR1 = dp->RXADDR0; /* Both buffers overlapped.*/ + } + else { + epr |= EPR_STAT_RX_NAK; + } +#else + epr |= EPR_STAT_RX_NAK; +#endif + } + + /* EPxR register setup.*/ + EPR_SET(ep, epr | ep); + EPR_TOGGLE(ep, epr); +} + +/** + * @brief Disables all the active endpoints except the endpoint zero. + * + * @param[in] usbp pointer to the @p USBDriver object + * + * @notapi + */ +void usb_lld_disable_endpoints(USBDriver *usbp) { + unsigned i; + + /* Resets the packet memory allocator.*/ + usb_pm_reset(usbp); + + /* Disabling all endpoints.*/ + for (i = 1; i <= USB_ENDOPOINTS_NUMBER; i++) { + EPR_TOGGLE(i, 0); + EPR_SET(i, 0); + } +} + +/** + * @brief Returns the status of an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { + + (void)usbp; + switch (STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) { + case EPR_STAT_RX_DIS: + return EP_STATUS_DISABLED; + case EPR_STAT_RX_STALL: + return EP_STATUS_STALLED; + default: + return EP_STATUS_ACTIVE; + } +} + +/** + * @brief Returns the status of an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return The endpoint status. + * @retval EP_STATUS_DISABLED The endpoint is not active. + * @retval EP_STATUS_STALLED The endpoint is stalled. + * @retval EP_STATUS_ACTIVE The endpoint is active. + * + * @notapi + */ +usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { + + (void)usbp; + switch (STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) { + case EPR_STAT_TX_DIS: + return EP_STATUS_DISABLED; + case EPR_STAT_TX_STALL: + return EP_STATUS_STALLED; + default: + return EP_STATUS_ACTIVE; + } +} + +/** + * @brief Reads a setup packet from the dedicated packet buffer. + * @details This function must be invoked in the context of the @p setup_cb + * callback in order to read the received setup packet. + * @pre In order to use this function the endpoint must have been + * initialized as a control endpoint. + * @post The endpoint is ready to accept another packet. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @param[out] buf buffer where to copy the packet data + * + * @notapi + */ +void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { + stm32_usb_pma_t *pmap; + stm32_usb_descriptor_t *udp; + uint32_t n; + + (void)usbp; + udp = USB_GET_DESCRIPTOR(ep); + pmap = USB_ADDR2PTR(udp->RXADDR0); + for (n = 0; n < 4; n++) { + *(uint16_t *)buf = (uint16_t)*pmap++; + buf += 2; + } +} + +/** + * @brief Starts a receive operation on an OUT endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { + USBOutEndpointState *osp = usbp->epc[ep]->out_state; + + /* Transfer initialization.*/ + if (osp->rxsize == 0) /* Special case for zero sized packets.*/ + osp->rxpkts = 1; + else + osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) / + usbp->epc[ep]->out_maxsize); + + EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID); +} + +/** + * @brief Starts a transmit operation on an IN endpoint. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { + size_t n; + USBInEndpointState *isp = usbp->epc[ep]->in_state; + + /* Transfer initialization.*/ + n = isp->txsize; + if (n > (size_t)usbp->epc[ep]->in_maxsize) + n = (size_t)usbp->epc[ep]->in_maxsize; + + isp->txlast = n; + usb_packet_write_from_buffer(ep, isp->txbuf, n); + + EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); +} + +/** + * @brief Brings an OUT endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { + + (void)usbp; + + EPR_SET_STAT_RX(ep, EPR_STAT_RX_STALL); +} + +/** + * @brief Brings an IN endpoint in the stalled state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { + + (void)usbp; + + EPR_SET_STAT_TX(ep, EPR_STAT_TX_STALL); +} + +/** + * @brief Brings an OUT endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { + + (void)usbp; + + /* Makes sure to not put to NAK an endpoint that is already + transferring.*/ + if ((STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) != EPR_STAT_RX_VALID) + EPR_SET_STAT_TX(ep, EPR_STAT_RX_NAK); +} + +/** + * @brief Brings an IN endpoint in the active state. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * + * @notapi + */ +void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { + + (void)usbp; + + /* Makes sure to not put to NAK an endpoint that is already + transferring.*/ + if ((STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) != EPR_STAT_TX_VALID) + EPR_SET_STAT_TX(ep, EPR_STAT_TX_NAK); +} + +#endif /* HAL_USE_USB */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.h b/os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.h new file mode 100644 index 000000000..e8db99f0f --- /dev/null +++ b/os/hal/ports/STM32/LLD/USBv1/hal_usb_lld.h @@ -0,0 +1,482 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file STM32/USBv1/usb_lld.h + * @brief STM32 USB subsystem low level driver header. + * + * @addtogroup USB + * @{ + */ + +#ifndef _USB_LLD_H_ +#define _USB_LLD_H_ + +#if HAL_USE_USB || defined(__DOXYGEN__) + +#include "stm32_usb.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @brief Maximum endpoint address. + */ +#define USB_MAX_ENDPOINTS USB_ENDOPOINTS_NUMBER + +/** + * @brief Status stage handling method. + */ +#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW + +/** + * @brief This device requires the address change after the status packet. + */ +#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS + +/** + * @brief Method for set address acknowledge. + */ +#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief USB1 driver enable switch. + * @details If set to @p TRUE the support for USB1 is included. + * @note The default is @p TRUE. + */ +#if !defined(STM32_USB_USE_USB1) || defined(__DOXYGEN__) +#define STM32_USB_USE_USB1 FALSE +#endif + +/** + * @brief Enables the USB device low power mode on suspend. + */ +#if !defined(STM32_USB_LOW_POWER_ON_SUSPEND) || defined(__DOXYGEN__) +#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE +#endif + +/** + * @brief USB1 interrupt priority level setting. + */ +#if (!defined(STM32_USB_USB1_HP_IRQ_PRIORITY) && \ + (STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER)) || defined(__DOXYGEN__) +#define STM32_USB_USB1_HP_IRQ_PRIORITY 13 +#endif + +/** + * @brief USB1 interrupt priority level setting. + */ +#if !defined(STM32_USB_USB1_LP_IRQ_PRIORITY) || defined(__DOXYGEN__) +#define STM32_USB_USB1_LP_IRQ_PRIORITY 14 +#endif + +/** + * @brief Enables isochronous support. + * @note Isochronous support requires special handling and this makes the + * code size increase significantly. + */ +#if !defined(STM32_USB_USE_ISOCHRONOUS) || defined(__DOXYGEN__) +#define STM32_USB_USE_ISOCHRONOUS FALSE +#endif + +/** + * @brief Use faster copy for packets. + * @note Makes the driver larger. + */ +#if !defined(STM32_USB_USE_FAST_COPY) || defined(__DOXYGEN__) +#define STM32_USB_USE_FAST_COPY FALSE +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_USB_USE_USB1 && !STM32_HAS_USB +#error "USB not present in the selected device" +#endif + +#if !STM32_USB_USE_USB1 +#error "USB driver activated but no USB peripheral assigned" +#endif + +#if STM32_USB_USE_USB1 && \ + (STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER) && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_USB1_HP_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USB HP" +#endif + +#if STM32_USB_USE_USB1 && \ + !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_USB1_LP_IRQ_PRIORITY) +#error "Invalid IRQ priority assigned to USB LP" +#endif + +#if STM32_USBCLK != 48000000 +#error "the USB driver requires a 48MHz clock" +#endif + +#if !defined(STM32_USB1_HP_HANDLER) +#error "STM32_USB1_HP_HANDLER not defined" +#endif + +#if !defined(STM32_USB1_HP_NUMBER) +#error "STM32_USB1_HP_NUMBER not defined" +#endif + +#if !defined(STM32_USB1_LP_HANDLER) +#error "STM32_USB1_LP_HANDLER not defined" +#endif + +#if !defined(STM32_USB1_LP_NUMBER) +#error "STM32_USB1_LP_NUMBER not defined" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of an IN endpoint state structure. + */ +typedef struct { + /** + * @brief Requested transmit transfer size. + */ + size_t txsize; + /** + * @brief Transmitted bytes so far. + */ + size_t txcnt; + /** + * @brief Pointer to the transmission linear buffer. + */ + const uint8_t *txbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Size of the last transmitted packet. + */ + size_t txlast; +} USBInEndpointState; + +/** + * @brief Type of an OUT endpoint state structure. + */ +typedef struct { + /** + * @brief Requested receive transfer size. + */ + size_t rxsize; + /** + * @brief Received bytes so far. + */ + size_t rxcnt; + /** + * @brief Pointer to the receive linear buffer. + */ + uint8_t *rxbuf; +#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) + /** + * @brief Waiting thread. + */ + thread_reference_t thread; +#endif + /* End of the mandatory fields.*/ + /** + * @brief Number of packets to receive. + */ + uint16_t rxpkts; +} USBOutEndpointState; + +/** + * @brief Type of an USB endpoint configuration structure. + * @note Platform specific restrictions may apply to endpoints. + */ +typedef struct { + /** + * @brief Type and mode of the endpoint. + */ + uint32_t ep_mode; + /** + * @brief Setup packet notification callback. + * @details This callback is invoked when a setup packet has been + * received. + * @post The application must immediately call @p usbReadPacket() in + * order to access the received packet. + * @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL + * endpoints, it should be set to @p NULL for other endpoint + * types. + */ + usbepcallback_t setup_cb; + /** + * @brief IN endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t in_cb; + /** + * @brief OUT endpoint notification callback. + * @details This field must be set to @p NULL if callback is not required. + */ + usbepcallback_t out_cb; + /** + * @brief IN endpoint maximum packet size. + * @details This field must be set to zero if the IN endpoint is not used. + */ + uint16_t in_maxsize; + /** + * @brief OUT endpoint maximum packet size. + * @details This field must be set to zero if the OUT endpoint is not used. + */ + uint16_t out_maxsize; + /** + * @brief @p USBEndpointState associated to the IN endpoint. + * @details This field must be set to @p NULL if the IN endpoint is not + * used. + */ + USBInEndpointState *in_state; + /** + * @brief @p USBEndpointState associated to the OUT endpoint. + * @details This field must be set to @p NULL if the OUT endpoint is not + * used. + */ + USBOutEndpointState *out_state; + /* End of the mandatory fields.*/ + /** + * @brief Reserved field, not currently used. + * @note Initialize this field to 1 in order to be forward compatible. + */ + uint16_t ep_buffers; + /** + * @brief Pointer to a buffer for setup packets. + * @details Setup packets require a dedicated 8-bytes buffer, set this + * field to @p NULL for non-control endpoints. + */ + uint8_t *setup_buf; +} USBEndpointConfig; + +/** + * @brief Type of an USB driver configuration structure. + */ +typedef struct { + /** + * @brief USB events callback. + * @details This callback is invoked when an USB driver event is registered. + */ + usbeventcb_t event_cb; + /** + * @brief Device GET_DESCRIPTOR request callback. + * @note This callback is mandatory and cannot be set to @p NULL. + */ + usbgetdescriptor_t get_descriptor_cb; + /** + * @brief Requests hook callback. + * @details This hook allows to be notified of standard requests or to + * handle non standard requests. + */ + usbreqhandler_t requests_hook_cb; + /** + * @brief Start Of Frame callback. + */ + usbcallback_t sof_cb; + /* End of the mandatory fields.*/ +} USBConfig; + +/** + * @brief Structure representing an USB driver. + */ +struct USBDriver { + /** + * @brief Driver state. + */ + usbstate_t state; + /** + * @brief Current configuration data. + */ + const USBConfig *config; + /** + * @brief Bit map of the transmitting IN endpoints. + */ + uint16_t transmitting; + /** + * @brief Bit map of the receiving OUT endpoints. + */ + uint16_t receiving; + /** + * @brief Active endpoints configurations. + */ + const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an IN endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *in_params[USB_MAX_ENDPOINTS]; + /** + * @brief Fields available to user, it can be used to associate an + * application-defined handler to an OUT endpoint. + * @note The base index is one, the endpoint zero does not have a + * reserved element in this array. + */ + void *out_params[USB_MAX_ENDPOINTS]; + /** + * @brief Endpoint 0 state. + */ + usbep0state_t ep0state; + /** + * @brief Next position in the buffer to be transferred through endpoint 0. + */ + uint8_t *ep0next; + /** + * @brief Number of bytes yet to be transferred through endpoint 0. + */ + size_t ep0n; + /** + * @brief Endpoint 0 end transaction callback. + */ + usbcallback_t ep0endcb; + /** + * @brief Setup packet buffer. + */ + uint8_t setup[8]; + /** + * @brief Current USB device status. + */ + uint16_t status; + /** + * @brief Assigned USB address. + */ + uint8_t address; + /** + * @brief Current USB device configuration. + */ + uint8_t configuration; +#if defined(USB_DRIVER_EXT_FIELDS) + USB_DRIVER_EXT_FIELDS +#endif + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the next address in the packet memory. + */ + uint32_t pmnext; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @brief Returns the current frame number. + * + * @param[in] usbp pointer to the @p USBDriver object + * @return The current frame number. + * + * @notapi + */ +#define usb_lld_get_frame_number(usbp) (STM32_USB->FNR & FNR_FN_MASK) + +/** + * @brief Returns the exact size of a receive transaction. + * @details The received size can be different from the size specified in + * @p usbStartReceiveI() because the last packet could have a size + * different from the expected one. + * @pre The OUT endpoint must have been configured in transaction mode + * in order to use this function. + * + * @param[in] usbp pointer to the @p USBDriver object + * @param[in] ep endpoint number + * @return Received data size. + * + * @notapi + */ +#define usb_lld_get_transaction_size(usbp, ep) \ + ((usbp)->epc[ep]->out_state->rxcnt) + +#if STM32_USB_HAS_BCDR || defined(__DOXYGEN__) +/** + * @brief Connects the USB device. + * + * @api + */ +#if !defined(usb_lld_connect_bus) +#define usb_lld_connect_bus(usbp) (STM32_USB->BCDR |= USB_BCDR_DPPU) +#endif + +/** + * @brief Disconnect the USB device. + * + * @api + */ +#if !defined(usb_lld_disconnect_bus) +#define usb_lld_disconnect_bus(usbp) (STM32_USB->BCDR &= ~USB_BCDR_DPPU) +#endif +#endif /* STM32_USB_HAS_BCDR */ + +#if defined(STM32L1XX) +#if !defined(usb_lld_connect_bus) +#define usb_lld_connect_bus(usbp) (SYSCFG->PMC |= SYSCFG_PMC_USB_PU) +#endif + +#if !defined(usb_lld_disconnect_bus) +#define usb_lld_disconnect_bus(usbp) (SYSCFG->PMC &= ~SYSCFG_PMC_USB_PU) +#endif +#endif /* STM32L1XX */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_USB_USE_USB1 && !defined(__DOXYGEN__) +extern USBDriver USBD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void usb_lld_init(void); + void usb_lld_start(USBDriver *usbp); + void usb_lld_stop(USBDriver *usbp); + void usb_lld_reset(USBDriver *usbp); + void usb_lld_set_address(USBDriver *usbp); + void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep); + void usb_lld_disable_endpoints(USBDriver *usbp); + usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep); + usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep); + void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf); + void usb_lld_start_out(USBDriver *usbp, usbep_t ep); + void usb_lld_start_in(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_out(USBDriver *usbp, usbep_t ep); + void usb_lld_stall_in(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_out(USBDriver *usbp, usbep_t ep); + void usb_lld_clear_in(USBDriver *usbp, usbep_t ep); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_USB */ + +#endif /* _USB_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/USBv1/usb_lld.c b/os/hal/ports/STM32/LLD/USBv1/usb_lld.c deleted file mode 100644 index 20bddbc94..000000000 --- a/os/hal/ports/STM32/LLD/USBv1/usb_lld.c +++ /dev/null @@ -1,863 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USBv1/usb_lld.c - * @brief STM32 USB subsystem low level driver source. - * - * @addtogroup USB - * @{ - */ - -#include - -#include "hal.h" - -#if HAL_USE_USB || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define BTABLE_ADDR 0x0000 - -#define EPR_EP_TYPE_IS_ISO(bits) ((bits & EPR_EP_TYPE_MASK) == EPR_EP_TYPE_ISO) - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/** @brief USB1 driver identifier.*/ -#if STM32_USB_USE_USB1 || defined(__DOXYGEN__) -USBDriver USBD1; -#endif - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -/** - * @brief EP0 state. - * @note It is an union because IN and OUT endpoints are never used at the - * same time for EP0. - */ -static union { - /** - * @brief IN EP0 state. - */ - USBInEndpointState in; - /** - * @brief OUT EP0 state. - */ - USBOutEndpointState out; -} ep0_state; - -/** - * @brief Buffer for the EP0 setup packets. - */ -static uint8_t ep0setup_buffer[8]; - -/** - * @brief EP0 initialization structure. - */ -static const USBEndpointConfig ep0config = { - USB_EP_MODE_TYPE_CTRL, - _usb_ep0setup, - _usb_ep0in, - _usb_ep0out, - 0x40, - 0x40, - &ep0_state.in, - &ep0_state.out, - 1, - ep0setup_buffer -}; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/** - * @brief Resets the packet memory allocator. - * - * @param[in] usbp pointer to the @p USBDriver object - */ -static void usb_pm_reset(USBDriver *usbp) { - - /* The first 64 bytes are reserved for the descriptors table. The effective - available RAM for endpoint buffers is just 448 bytes.*/ - usbp->pmnext = 64; -} - -/** - * @brief Resets the packet memory allocator. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] size size of the packet buffer to allocate - */ -static uint32_t usb_pm_alloc(USBDriver *usbp, size_t size) { - uint32_t next; - - next = usbp->pmnext; - usbp->pmnext += (size + 1) & ~1; - osalDbgAssert(usbp->pmnext <= STM32_USB_PMA_SIZE, "PMA overflow"); - return next; -} - -/** - * @brief Reads from a dedicated packet buffer. - * - * @param[in] udp pointer to a @p stm32_usb_descriptor_t - * @param[out] buf buffer where to copy the packet data - * @return The size of the receivee packet. - * - * @notapi - */ -static size_t usb_packet_read_to_buffer(usbep_t ep, uint8_t *buf) { - size_t i, n; - stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); - stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->RXADDR0); -#if STM32_USB_USE_ISOCHRONOUS - uint32_t epr = STM32_USB->EPR[ep]; - - /* Double buffering is always enabled for isochronous endpoints, and - although we overlap the two buffers for simplicity, we still need - to read from the right counter. The DTOG_RX bit indicates the buffer - that is currently in use by the USB peripheral, that is, the buffer - in which the next received packet will be stored, so we need to - read the counter of the OTHER buffer, which is where the last - received packet was stored.*/ - if (EPR_EP_TYPE_IS_ISO(epr) && !(epr & EPR_DTOG_RX)) - n = (size_t)udp->RXCOUNT1 & RXCOUNT_COUNT_MASK; - else - n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; -#else - n = (size_t)udp->RXCOUNT0 & RXCOUNT_COUNT_MASK; -#endif - - i = n; - -#if STM32_USB_USE_FAST_COPY - while (i >= 16) { - uint32_t w; - - w = *(pmap + 0); - *(buf + 0) = (uint8_t)w; - *(buf + 1) = (uint8_t)(w >> 8); - w = *(pmap + 1); - *(buf + 2) = (uint8_t)w; - *(buf + 3) = (uint8_t)(w >> 8); - w = *(pmap + 2); - *(buf + 4) = (uint8_t)w; - *(buf + 5) = (uint8_t)(w >> 8); - w = *(pmap + 3); - *(buf + 6) = (uint8_t)w; - *(buf + 7) = (uint8_t)(w >> 8); - w = *(pmap + 4); - *(buf + 8) = (uint8_t)w; - *(buf + 9) = (uint8_t)(w >> 8); - w = *(pmap + 5); - *(buf + 10) = (uint8_t)w; - *(buf + 11) = (uint8_t)(w >> 8); - w = *(pmap + 6); - *(buf + 12) = (uint8_t)w; - *(buf + 13) = (uint8_t)(w >> 8); - w = *(pmap + 7); - *(buf + 14) = (uint8_t)w; - *(buf + 15) = (uint8_t)(w >> 8); - - i -= 16; - buf += 16; - pmap += 8; - } -#endif /* STM32_USB_USE_FAST_COPY */ - - while (i >= 2) { - uint32_t w = *pmap++; - *buf++ = (uint8_t)w; - *buf++ = (uint8_t)(w >> 8); - i -= 2; - } - - if (i >= 1) { - *buf = (uint8_t)*pmap; - } - - return n; -} - -/** - * @brief Writes to a dedicated packet buffer. - * - * @param[in] ep endpoint number - * @param[in] buf buffer where to fetch the packet data - * @param[in] n maximum number of bytes to copy. This value must - * not exceed the maximum packet size for this endpoint. - * - * @notapi - */ -static void usb_packet_write_from_buffer(usbep_t ep, - const uint8_t *buf, - size_t n) { - stm32_usb_descriptor_t *udp = USB_GET_DESCRIPTOR(ep); - stm32_usb_pma_t *pmap = USB_ADDR2PTR(udp->TXADDR0); - int i = (int)n; - -#if STM32_USB_USE_ISOCHRONOUS - uint32_t epr = STM32_USB->EPR[ep]; - - /* Double buffering is always enabled for isochronous endpoints, and - although we overlap the two buffers for simplicity, we still need - to write to the right counter. The DTOG_TX bit indicates the buffer - that is currently in use by the USB peripheral, that is, the buffer - from which the next packet will be sent, so we need to write the - counter of that buffer.*/ - if (EPR_EP_TYPE_IS_ISO(epr) && (epr & EPR_DTOG_TX)) - udp->TXCOUNT1 = (stm32_usb_pma_t)n; - else - udp->TXCOUNT0 = (stm32_usb_pma_t)n; -#else - udp->TXCOUNT0 = (stm32_usb_pma_t)n; -#endif - -#if STM32_USB_USE_FAST_COPY - while (i >= 16) { - uint32_t w; - - w = *(buf + 0); - w |= *(buf + 1) << 8; - *(pmap + 0) = (stm32_usb_pma_t)w; - w = *(buf + 2); - w |= *(buf + 3) << 8; - *(pmap + 1) = (stm32_usb_pma_t)w; - w = *(buf + 4); - w |= *(buf + 5) << 8; - *(pmap + 2) = (stm32_usb_pma_t)w; - w = *(buf + 6); - w |= *(buf + 7) << 8; - *(pmap + 3) = (stm32_usb_pma_t)w; - w = *(buf + 8); - w |= *(buf + 9) << 8; - *(pmap + 4) = (stm32_usb_pma_t)w; - w = *(buf + 10); - w |= *(buf + 11) << 8; - *(pmap + 5) = (stm32_usb_pma_t)w; - w = *(buf + 12); - w |= *(buf + 13) << 8; - *(pmap + 6) = (stm32_usb_pma_t)w; - w = *(buf + 14); - w |= *(buf + 15) << 8; - *(pmap + 7) = (stm32_usb_pma_t)w; - - i -= 16; - buf += 16; - pmap += 8; - } -#endif /* STM32_USB_USE_FAST_COPY */ - - while (i > 0) { - uint32_t w; - - w = *buf++; - w |= *buf++ << 8; - *pmap++ = (stm32_usb_pma_t)w; - i -= 2; - } -} - -/** - * @brief Common ISR code, serves the EP-related interrupts. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -static void usb_serve_endpoints(USBDriver *usbp, uint32_t ep) { - size_t n; - uint32_t epr = STM32_USB->EPR[ep]; - const USBEndpointConfig *epcp = usbp->epc[ep]; - - if (epr & EPR_CTR_TX) { - /* IN endpoint, transmission.*/ - USBInEndpointState *isp = epcp->in_state; - - EPR_CLEAR_CTR_TX(ep); - - isp->txcnt += isp->txlast; - n = isp->txsize - isp->txcnt; - if (n > 0) { - /* Transfer not completed, there are more packets to send.*/ - if (n > epcp->in_maxsize) - n = epcp->in_maxsize; - - /* Writes the packet from the defined buffer.*/ - isp->txbuf += isp->txlast; - isp->txlast = n; - usb_packet_write_from_buffer(ep, isp->txbuf, n); - - /* Starting IN operation.*/ - EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); - } - else { - /* Transfer completed, invokes the callback.*/ - _usb_isr_invoke_in_cb(usbp, ep); - } - } - if (epr & EPR_CTR_RX) { - /* OUT endpoint, receive.*/ - - EPR_CLEAR_CTR_RX(ep); - - if (epr & EPR_SETUP) { - /* Setup packets handling, setup packets are handled using a - specific callback.*/ - _usb_isr_invoke_setup_cb(usbp, ep); - } - else { - USBOutEndpointState *osp = epcp->out_state; - - /* Reads the packet into the defined buffer.*/ - n = usb_packet_read_to_buffer(ep, osp->rxbuf); - osp->rxbuf += n; - - /* Transaction data updated.*/ - osp->rxcnt += n; - osp->rxsize -= n; - osp->rxpkts -= 1; - - /* The transaction is completed if the specified number of packets - has been received or the current packet is a short packet.*/ - if ((n < epcp->out_maxsize) || (osp->rxpkts == 0)) { - /* Transfer complete, invokes the callback.*/ - _usb_isr_invoke_out_cb(usbp, ep); - } - else { - /* Transfer not complete, there are more packets to receive.*/ - EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID); - } - } - } -} - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -#if STM32_USB_USE_USB1 || defined(__DOXYGEN__) -#if STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER -#if STM32_USB_USE_ISOCHRONOUS -/** - * @brief USB high priority interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USB1_HP_HANDLER) { - uint32_t istr; - USBDriver *usbp = &USBD1; - - OSAL_IRQ_PROLOGUE(); - - /* Endpoint events handling.*/ - istr = STM32_USB->ISTR; - while (istr & ISTR_CTR) { - usb_serve_endpoints(usbp, istr & ISTR_EP_ID_MASK); - istr = STM32_USB->ISTR; - } - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_USB_USE_ISOCHRONOUS */ -#endif /* STM32_USB1_LP_NUMBER != STM32_USB1_HP_NUMBER */ - -/** - * @brief USB low priority interrupt handler. - * - * @isr - */ -OSAL_IRQ_HANDLER(STM32_USB1_LP_HANDLER) { - uint32_t istr; - USBDriver *usbp = &USBD1; - - OSAL_IRQ_PROLOGUE(); - - istr = STM32_USB->ISTR; - - /* USB bus reset condition handling.*/ - if (istr & ISTR_RESET) { - STM32_USB->ISTR = ~ISTR_RESET; - - _usb_reset(usbp); - } - - /* USB bus SUSPEND condition handling.*/ - if (istr & ISTR_SUSP) { - STM32_USB->CNTR |= CNTR_FSUSP; -#if STM32_USB_LOW_POWER_ON_SUSPEND - STM32_USB->CNTR |= CNTR_LP_MODE; -#endif - STM32_USB->ISTR = ~ISTR_SUSP; - - _usb_suspend(usbp); - } - - /* USB bus WAKEUP condition handling.*/ - if (istr & ISTR_WKUP) { - uint32_t fnr = STM32_USB->FNR; - if (!(fnr & FNR_RXDP)) { - STM32_USB->CNTR &= ~CNTR_FSUSP; - - _usb_wakeup(usbp); - } -#if STM32_USB_LOW_POWER_ON_SUSPEND - else { - /* Just noise, going back in SUSPEND mode, reference manual 22.4.5, - table 169.*/ - STM32_USB->CNTR |= CNTR_LP_MODE; - } -#endif - STM32_USB->ISTR = ~ISTR_WKUP; - } - - /* SOF handling.*/ - if (istr & ISTR_SOF) { - _usb_isr_invoke_sof_cb(usbp); - STM32_USB->ISTR = ~ISTR_SOF; - } - - /* Endpoint events handling.*/ - while (istr & ISTR_CTR) { - usb_serve_endpoints(usbp, istr & ISTR_EP_ID_MASK); - istr = STM32_USB->ISTR; - } - - OSAL_IRQ_EPILOGUE(); -} -#endif /* STM32_USB_USE_USB1 */ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level USB driver initialization. - * - * @notapi - */ -void usb_lld_init(void) { - - /* Driver initialization.*/ - usbObjectInit(&USBD1); -} - -/** - * @brief Configures and activates the USB peripheral. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_start(USBDriver *usbp) { - - if (usbp->state == USB_STOP) { - /* Clock activation.*/ -#if STM32_USB_USE_USB1 - if (&USBD1 == usbp) { - /* USB clock enabled.*/ - rccEnableUSB(FALSE); - /* Powers up the transceiver while holding the USB in reset state.*/ - STM32_USB->CNTR = CNTR_FRES; - /* Enabling the USB IRQ vectors, this also gives enough time to allow - the transceiver power up (1uS).*/ -#if STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER - nvicEnableVector(STM32_USB1_HP_NUMBER, STM32_USB_USB1_HP_IRQ_PRIORITY); -#endif - nvicEnableVector(STM32_USB1_LP_NUMBER, STM32_USB_USB1_LP_IRQ_PRIORITY); - /* Releases the USB reset.*/ - STM32_USB->CNTR = 0; - } -#endif - /* Reset procedure enforced on driver start.*/ - _usb_reset(usbp); - } -} - -/** - * @brief Deactivates the USB peripheral. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_stop(USBDriver *usbp) { - - /* If in ready state then disables the USB clock.*/ - if (usbp->state == USB_STOP) { -#if STM32_USB_USE_USB1 - if (&USBD1 == usbp) { -#if STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER - nvicDisableVector(STM32_USB1_HP_NUMBER); -#endif - nvicDisableVector(STM32_USB1_LP_NUMBER); - STM32_USB->CNTR = CNTR_PDWN | CNTR_FRES; - rccDisableUSB(FALSE); - } -#endif - } -} - -/** - * @brief USB low level reset routine. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_reset(USBDriver *usbp) { - uint32_t cntr; - - /* Post reset initialization.*/ - STM32_USB->BTABLE = BTABLE_ADDR; - STM32_USB->ISTR = 0; - STM32_USB->DADDR = DADDR_EF; - cntr = /*CNTR_ESOFM | */ CNTR_RESETM | CNTR_SUSPM | - CNTR_WKUPM | /*CNTR_ERRM | CNTR_PMAOVRM |*/ CNTR_CTRM; - /* The SOF interrupt is only enabled if a callback is defined for - this service because it is an high rate source.*/ - if (usbp->config->sof_cb != NULL) - cntr |= CNTR_SOFM; - STM32_USB->CNTR = cntr; - - /* Resets the packet memory allocator.*/ - usb_pm_reset(usbp); - - /* EP0 initialization.*/ - usbp->epc[0] = &ep0config; - usb_lld_init_endpoint(usbp, 0); -} - -/** - * @brief Sets the USB address. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_set_address(USBDriver *usbp) { - - STM32_USB->DADDR = (uint32_t)(usbp->address) | DADDR_EF; -} - -/** - * @brief Enables an endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { - uint16_t epr; - stm32_usb_descriptor_t *dp; - const USBEndpointConfig *epcp = usbp->epc[ep]; - - /* Setting the endpoint type. Note that isochronous endpoints cannot be - bidirectional because it uses double buffering and both transmit and - receive descriptor fields are used for either direction.*/ - switch (epcp->ep_mode & USB_EP_MODE_TYPE) { - case USB_EP_MODE_TYPE_ISOC: -#if STM32_USB_USE_ISOCHRONOUS - osalDbgAssert((epcp->in_state == NULL) || (epcp->out_state == NULL), - "isochronous EP cannot be IN and OUT"); - epr = EPR_EP_TYPE_ISO; - break; -#else - osalDbgAssert(false, "isochronous support disabled"); -#endif - case USB_EP_MODE_TYPE_BULK: - epr = EPR_EP_TYPE_BULK; - break; - case USB_EP_MODE_TYPE_INTR: - epr = EPR_EP_TYPE_INTERRUPT; - break; - default: - epr = EPR_EP_TYPE_CONTROL; - } - - dp = USB_GET_DESCRIPTOR(ep); - - /* IN endpoint handling.*/ - if (epcp->in_state != NULL) { - dp->TXCOUNT0 = 0; - dp->TXADDR0 = usb_pm_alloc(usbp, epcp->in_maxsize); - -#if STM32_USB_USE_ISOCHRONOUS - if (epr == EPR_EP_TYPE_ISO) { - epr |= EPR_STAT_TX_VALID; - dp->TXCOUNT1 = dp->TXCOUNT0; - dp->TXADDR1 = dp->TXADDR0; /* Both buffers overlapped.*/ - } - else { - epr |= EPR_STAT_TX_NAK; - } -#else - epr |= EPR_STAT_TX_NAK; -#endif - } - - /* OUT endpoint handling.*/ - if (epcp->out_state != NULL) { - uint16_t nblocks; - - /* Endpoint size and address initialization.*/ - if (epcp->out_maxsize > 62) - nblocks = (((((epcp->out_maxsize - 1) | 0x1f) + 1) / 32) << 10) | - 0x8000; - else - nblocks = ((((epcp->out_maxsize - 1) | 1) + 1) / 2) << 10; - dp->RXCOUNT0 = nblocks; - dp->RXADDR0 = usb_pm_alloc(usbp, epcp->out_maxsize); - -#if STM32_USB_USE_ISOCHRONOUS - if (epr == EPR_EP_TYPE_ISO) { - epr |= EPR_STAT_RX_VALID; - dp->RXCOUNT1 = dp->RXCOUNT0; - dp->RXADDR1 = dp->RXADDR0; /* Both buffers overlapped.*/ - } - else { - epr |= EPR_STAT_RX_NAK; - } -#else - epr |= EPR_STAT_RX_NAK; -#endif - } - - /* EPxR register setup.*/ - EPR_SET(ep, epr | ep); - EPR_TOGGLE(ep, epr); -} - -/** - * @brief Disables all the active endpoints except the endpoint zero. - * - * @param[in] usbp pointer to the @p USBDriver object - * - * @notapi - */ -void usb_lld_disable_endpoints(USBDriver *usbp) { - unsigned i; - - /* Resets the packet memory allocator.*/ - usb_pm_reset(usbp); - - /* Disabling all endpoints.*/ - for (i = 1; i <= USB_ENDOPOINTS_NUMBER; i++) { - EPR_TOGGLE(i, 0); - EPR_SET(i, 0); - } -} - -/** - * @brief Returns the status of an OUT endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @return The endpoint status. - * @retval EP_STATUS_DISABLED The endpoint is not active. - * @retval EP_STATUS_STALLED The endpoint is stalled. - * @retval EP_STATUS_ACTIVE The endpoint is active. - * - * @notapi - */ -usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) { - - (void)usbp; - switch (STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) { - case EPR_STAT_RX_DIS: - return EP_STATUS_DISABLED; - case EPR_STAT_RX_STALL: - return EP_STATUS_STALLED; - default: - return EP_STATUS_ACTIVE; - } -} - -/** - * @brief Returns the status of an IN endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @return The endpoint status. - * @retval EP_STATUS_DISABLED The endpoint is not active. - * @retval EP_STATUS_STALLED The endpoint is stalled. - * @retval EP_STATUS_ACTIVE The endpoint is active. - * - * @notapi - */ -usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) { - - (void)usbp; - switch (STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) { - case EPR_STAT_TX_DIS: - return EP_STATUS_DISABLED; - case EPR_STAT_TX_STALL: - return EP_STATUS_STALLED; - default: - return EP_STATUS_ACTIVE; - } -} - -/** - * @brief Reads a setup packet from the dedicated packet buffer. - * @details This function must be invoked in the context of the @p setup_cb - * callback in order to read the received setup packet. - * @pre In order to use this function the endpoint must have been - * initialized as a control endpoint. - * @post The endpoint is ready to accept another packet. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @param[out] buf buffer where to copy the packet data - * - * @notapi - */ -void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { - stm32_usb_pma_t *pmap; - stm32_usb_descriptor_t *udp; - uint32_t n; - - (void)usbp; - udp = USB_GET_DESCRIPTOR(ep); - pmap = USB_ADDR2PTR(udp->RXADDR0); - for (n = 0; n < 4; n++) { - *(uint16_t *)buf = (uint16_t)*pmap++; - buf += 2; - } -} - -/** - * @brief Starts a receive operation on an OUT endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { - USBOutEndpointState *osp = usbp->epc[ep]->out_state; - - /* Transfer initialization.*/ - if (osp->rxsize == 0) /* Special case for zero sized packets.*/ - osp->rxpkts = 1; - else - osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) / - usbp->epc[ep]->out_maxsize); - - EPR_SET_STAT_RX(ep, EPR_STAT_RX_VALID); -} - -/** - * @brief Starts a transmit operation on an IN endpoint. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { - size_t n; - USBInEndpointState *isp = usbp->epc[ep]->in_state; - - /* Transfer initialization.*/ - n = isp->txsize; - if (n > (size_t)usbp->epc[ep]->in_maxsize) - n = (size_t)usbp->epc[ep]->in_maxsize; - - isp->txlast = n; - usb_packet_write_from_buffer(ep, isp->txbuf, n); - - EPR_SET_STAT_TX(ep, EPR_STAT_TX_VALID); -} - -/** - * @brief Brings an OUT endpoint in the stalled state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) { - - (void)usbp; - - EPR_SET_STAT_RX(ep, EPR_STAT_RX_STALL); -} - -/** - * @brief Brings an IN endpoint in the stalled state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) { - - (void)usbp; - - EPR_SET_STAT_TX(ep, EPR_STAT_TX_STALL); -} - -/** - * @brief Brings an OUT endpoint in the active state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) { - - (void)usbp; - - /* Makes sure to not put to NAK an endpoint that is already - transferring.*/ - if ((STM32_USB->EPR[ep] & EPR_STAT_RX_MASK) != EPR_STAT_RX_VALID) - EPR_SET_STAT_TX(ep, EPR_STAT_RX_NAK); -} - -/** - * @brief Brings an IN endpoint in the active state. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * - * @notapi - */ -void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) { - - (void)usbp; - - /* Makes sure to not put to NAK an endpoint that is already - transferring.*/ - if ((STM32_USB->EPR[ep] & EPR_STAT_TX_MASK) != EPR_STAT_TX_VALID) - EPR_SET_STAT_TX(ep, EPR_STAT_TX_NAK); -} - -#endif /* HAL_USE_USB */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/USBv1/usb_lld.h b/os/hal/ports/STM32/LLD/USBv1/usb_lld.h deleted file mode 100644 index e8db99f0f..000000000 --- a/os/hal/ports/STM32/LLD/USBv1/usb_lld.h +++ /dev/null @@ -1,482 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file STM32/USBv1/usb_lld.h - * @brief STM32 USB subsystem low level driver header. - * - * @addtogroup USB - * @{ - */ - -#ifndef _USB_LLD_H_ -#define _USB_LLD_H_ - -#if HAL_USE_USB || defined(__DOXYGEN__) - -#include "stm32_usb.h" - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @brief Maximum endpoint address. - */ -#define USB_MAX_ENDPOINTS USB_ENDOPOINTS_NUMBER - -/** - * @brief Status stage handling method. - */ -#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW - -/** - * @brief This device requires the address change after the status packet. - */ -#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS - -/** - * @brief Method for set address acknowledge. - */ -#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @brief USB1 driver enable switch. - * @details If set to @p TRUE the support for USB1 is included. - * @note The default is @p TRUE. - */ -#if !defined(STM32_USB_USE_USB1) || defined(__DOXYGEN__) -#define STM32_USB_USE_USB1 FALSE -#endif - -/** - * @brief Enables the USB device low power mode on suspend. - */ -#if !defined(STM32_USB_LOW_POWER_ON_SUSPEND) || defined(__DOXYGEN__) -#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE -#endif - -/** - * @brief USB1 interrupt priority level setting. - */ -#if (!defined(STM32_USB_USB1_HP_IRQ_PRIORITY) && \ - (STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER)) || defined(__DOXYGEN__) -#define STM32_USB_USB1_HP_IRQ_PRIORITY 13 -#endif - -/** - * @brief USB1 interrupt priority level setting. - */ -#if !defined(STM32_USB_USB1_LP_IRQ_PRIORITY) || defined(__DOXYGEN__) -#define STM32_USB_USB1_LP_IRQ_PRIORITY 14 -#endif - -/** - * @brief Enables isochronous support. - * @note Isochronous support requires special handling and this makes the - * code size increase significantly. - */ -#if !defined(STM32_USB_USE_ISOCHRONOUS) || defined(__DOXYGEN__) -#define STM32_USB_USE_ISOCHRONOUS FALSE -#endif - -/** - * @brief Use faster copy for packets. - * @note Makes the driver larger. - */ -#if !defined(STM32_USB_USE_FAST_COPY) || defined(__DOXYGEN__) -#define STM32_USB_USE_FAST_COPY FALSE -#endif - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_USB_USE_USB1 && !STM32_HAS_USB -#error "USB not present in the selected device" -#endif - -#if !STM32_USB_USE_USB1 -#error "USB driver activated but no USB peripheral assigned" -#endif - -#if STM32_USB_USE_USB1 && \ - (STM32_USB1_HP_NUMBER != STM32_USB1_LP_NUMBER) && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_USB1_HP_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USB HP" -#endif - -#if STM32_USB_USE_USB1 && \ - !OSAL_IRQ_IS_VALID_PRIORITY(STM32_USB_USB1_LP_IRQ_PRIORITY) -#error "Invalid IRQ priority assigned to USB LP" -#endif - -#if STM32_USBCLK != 48000000 -#error "the USB driver requires a 48MHz clock" -#endif - -#if !defined(STM32_USB1_HP_HANDLER) -#error "STM32_USB1_HP_HANDLER not defined" -#endif - -#if !defined(STM32_USB1_HP_NUMBER) -#error "STM32_USB1_HP_NUMBER not defined" -#endif - -#if !defined(STM32_USB1_LP_HANDLER) -#error "STM32_USB1_LP_HANDLER not defined" -#endif - -#if !defined(STM32_USB1_LP_NUMBER) -#error "STM32_USB1_LP_NUMBER not defined" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of an IN endpoint state structure. - */ -typedef struct { - /** - * @brief Requested transmit transfer size. - */ - size_t txsize; - /** - * @brief Transmitted bytes so far. - */ - size_t txcnt; - /** - * @brief Pointer to the transmission linear buffer. - */ - const uint8_t *txbuf; -#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif - /* End of the mandatory fields.*/ - /** - * @brief Size of the last transmitted packet. - */ - size_t txlast; -} USBInEndpointState; - -/** - * @brief Type of an OUT endpoint state structure. - */ -typedef struct { - /** - * @brief Requested receive transfer size. - */ - size_t rxsize; - /** - * @brief Received bytes so far. - */ - size_t rxcnt; - /** - * @brief Pointer to the receive linear buffer. - */ - uint8_t *rxbuf; -#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__) - /** - * @brief Waiting thread. - */ - thread_reference_t thread; -#endif - /* End of the mandatory fields.*/ - /** - * @brief Number of packets to receive. - */ - uint16_t rxpkts; -} USBOutEndpointState; - -/** - * @brief Type of an USB endpoint configuration structure. - * @note Platform specific restrictions may apply to endpoints. - */ -typedef struct { - /** - * @brief Type and mode of the endpoint. - */ - uint32_t ep_mode; - /** - * @brief Setup packet notification callback. - * @details This callback is invoked when a setup packet has been - * received. - * @post The application must immediately call @p usbReadPacket() in - * order to access the received packet. - * @note This field is only valid for @p USB_EP_MODE_TYPE_CTRL - * endpoints, it should be set to @p NULL for other endpoint - * types. - */ - usbepcallback_t setup_cb; - /** - * @brief IN endpoint notification callback. - * @details This field must be set to @p NULL if callback is not required. - */ - usbepcallback_t in_cb; - /** - * @brief OUT endpoint notification callback. - * @details This field must be set to @p NULL if callback is not required. - */ - usbepcallback_t out_cb; - /** - * @brief IN endpoint maximum packet size. - * @details This field must be set to zero if the IN endpoint is not used. - */ - uint16_t in_maxsize; - /** - * @brief OUT endpoint maximum packet size. - * @details This field must be set to zero if the OUT endpoint is not used. - */ - uint16_t out_maxsize; - /** - * @brief @p USBEndpointState associated to the IN endpoint. - * @details This field must be set to @p NULL if the IN endpoint is not - * used. - */ - USBInEndpointState *in_state; - /** - * @brief @p USBEndpointState associated to the OUT endpoint. - * @details This field must be set to @p NULL if the OUT endpoint is not - * used. - */ - USBOutEndpointState *out_state; - /* End of the mandatory fields.*/ - /** - * @brief Reserved field, not currently used. - * @note Initialize this field to 1 in order to be forward compatible. - */ - uint16_t ep_buffers; - /** - * @brief Pointer to a buffer for setup packets. - * @details Setup packets require a dedicated 8-bytes buffer, set this - * field to @p NULL for non-control endpoints. - */ - uint8_t *setup_buf; -} USBEndpointConfig; - -/** - * @brief Type of an USB driver configuration structure. - */ -typedef struct { - /** - * @brief USB events callback. - * @details This callback is invoked when an USB driver event is registered. - */ - usbeventcb_t event_cb; - /** - * @brief Device GET_DESCRIPTOR request callback. - * @note This callback is mandatory and cannot be set to @p NULL. - */ - usbgetdescriptor_t get_descriptor_cb; - /** - * @brief Requests hook callback. - * @details This hook allows to be notified of standard requests or to - * handle non standard requests. - */ - usbreqhandler_t requests_hook_cb; - /** - * @brief Start Of Frame callback. - */ - usbcallback_t sof_cb; - /* End of the mandatory fields.*/ -} USBConfig; - -/** - * @brief Structure representing an USB driver. - */ -struct USBDriver { - /** - * @brief Driver state. - */ - usbstate_t state; - /** - * @brief Current configuration data. - */ - const USBConfig *config; - /** - * @brief Bit map of the transmitting IN endpoints. - */ - uint16_t transmitting; - /** - * @brief Bit map of the receiving OUT endpoints. - */ - uint16_t receiving; - /** - * @brief Active endpoints configurations. - */ - const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1]; - /** - * @brief Fields available to user, it can be used to associate an - * application-defined handler to an IN endpoint. - * @note The base index is one, the endpoint zero does not have a - * reserved element in this array. - */ - void *in_params[USB_MAX_ENDPOINTS]; - /** - * @brief Fields available to user, it can be used to associate an - * application-defined handler to an OUT endpoint. - * @note The base index is one, the endpoint zero does not have a - * reserved element in this array. - */ - void *out_params[USB_MAX_ENDPOINTS]; - /** - * @brief Endpoint 0 state. - */ - usbep0state_t ep0state; - /** - * @brief Next position in the buffer to be transferred through endpoint 0. - */ - uint8_t *ep0next; - /** - * @brief Number of bytes yet to be transferred through endpoint 0. - */ - size_t ep0n; - /** - * @brief Endpoint 0 end transaction callback. - */ - usbcallback_t ep0endcb; - /** - * @brief Setup packet buffer. - */ - uint8_t setup[8]; - /** - * @brief Current USB device status. - */ - uint16_t status; - /** - * @brief Assigned USB address. - */ - uint8_t address; - /** - * @brief Current USB device configuration. - */ - uint8_t configuration; -#if defined(USB_DRIVER_EXT_FIELDS) - USB_DRIVER_EXT_FIELDS -#endif - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the next address in the packet memory. - */ - uint32_t pmnext; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/** - * @brief Returns the current frame number. - * - * @param[in] usbp pointer to the @p USBDriver object - * @return The current frame number. - * - * @notapi - */ -#define usb_lld_get_frame_number(usbp) (STM32_USB->FNR & FNR_FN_MASK) - -/** - * @brief Returns the exact size of a receive transaction. - * @details The received size can be different from the size specified in - * @p usbStartReceiveI() because the last packet could have a size - * different from the expected one. - * @pre The OUT endpoint must have been configured in transaction mode - * in order to use this function. - * - * @param[in] usbp pointer to the @p USBDriver object - * @param[in] ep endpoint number - * @return Received data size. - * - * @notapi - */ -#define usb_lld_get_transaction_size(usbp, ep) \ - ((usbp)->epc[ep]->out_state->rxcnt) - -#if STM32_USB_HAS_BCDR || defined(__DOXYGEN__) -/** - * @brief Connects the USB device. - * - * @api - */ -#if !defined(usb_lld_connect_bus) -#define usb_lld_connect_bus(usbp) (STM32_USB->BCDR |= USB_BCDR_DPPU) -#endif - -/** - * @brief Disconnect the USB device. - * - * @api - */ -#if !defined(usb_lld_disconnect_bus) -#define usb_lld_disconnect_bus(usbp) (STM32_USB->BCDR &= ~USB_BCDR_DPPU) -#endif -#endif /* STM32_USB_HAS_BCDR */ - -#if defined(STM32L1XX) -#if !defined(usb_lld_connect_bus) -#define usb_lld_connect_bus(usbp) (SYSCFG->PMC |= SYSCFG_PMC_USB_PU) -#endif - -#if !defined(usb_lld_disconnect_bus) -#define usb_lld_disconnect_bus(usbp) (SYSCFG->PMC &= ~SYSCFG_PMC_USB_PU) -#endif -#endif /* STM32L1XX */ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_USB_USE_USB1 && !defined(__DOXYGEN__) -extern USBDriver USBD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void usb_lld_init(void); - void usb_lld_start(USBDriver *usbp); - void usb_lld_stop(USBDriver *usbp); - void usb_lld_reset(USBDriver *usbp); - void usb_lld_set_address(USBDriver *usbp); - void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep); - void usb_lld_disable_endpoints(USBDriver *usbp); - usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep); - usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep); - void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf); - void usb_lld_start_out(USBDriver *usbp, usbep_t ep); - void usb_lld_start_in(USBDriver *usbp, usbep_t ep); - void usb_lld_stall_out(USBDriver *usbp, usbep_t ep); - void usb_lld_stall_in(USBDriver *usbp, usbep_t ep); - void usb_lld_clear_out(USBDriver *usbp, usbep_t ep); - void usb_lld_clear_in(USBDriver *usbp, usbep_t ep); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_USB */ - -#endif /* _USB_LLD_H_ */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.c b/os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.c new file mode 100644 index 000000000..15cfefd72 --- /dev/null +++ b/os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.c @@ -0,0 +1,140 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file wdg_lld.c + * @brief WDG Driver subsystem low level driver source. + * + * @addtogroup WDG + * @{ + */ + +#include "hal.h" + +#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define KR_KEY_RELOAD 0xAAAAU +#define KR_KEY_ENABLE 0xCCCCU +#define KR_KEY_WRITE 0x5555U +#define KR_KEY_PROTECT 0x0000U + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +#if STM32_WDG_USE_IWDG || defined(__DOXYGEN__) +WDGDriver WDGD1; +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver interrupt handlers. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Low level WDG driver initialization. + * + * @notapi + */ +void wdg_lld_init(void) { + +#if STM32_WDG_USE_IWDG + WDGD1.state = WDG_STOP; + WDGD1.wdg = IWDG; +#endif +} + +/** + * @brief Configures and activates the WDG peripheral. + * + * @param[in] wdgp pointer to the @p WDGDriver object + * + * @notapi + */ +void wdg_lld_start(WDGDriver *wdgp) { + +#if STM32_IWDG_IS_WINDOWED + /* Enable IWDG and unlock for write.*/ + wdgp->wdg->KR = KR_KEY_ENABLE; + wdgp->wdg->KR = KR_KEY_WRITE; + + /* Write configuration.*/ + wdgp->wdg->PR = wdgp->config->pr; + wdgp->wdg->RLR = wdgp->config->rlr; + while (wdgp->wdg->SR != 0) + ; + + /* This also triggers a refresh.*/ + wdgp->wdg->WINR = wdgp->config->winr; +#else + /* Unlock IWDG.*/ + wdgp->wdg->KR = KR_KEY_WRITE; + + /* Write configuration.*/ + while (wdgp->wdg->SR != 0) + ; + wdgp->wdg->PR = wdgp->config->pr; + wdgp->wdg->RLR = wdgp->config->rlr; + + /* Start operations.*/ + wdgp->wdg->KR = KR_KEY_RELOAD; + wdgp->wdg->KR = KR_KEY_ENABLE; +#endif +} + +/** + * @brief Deactivates the WDG peripheral. + * + * @param[in] wdgp pointer to the @p WDGDriver object + * + * @api + */ +void wdg_lld_stop(WDGDriver *wdgp) { + + osalDbgAssert(wdgp->state == WDG_STOP, + "IWDG cannot be stopped once activated"); +} + +/** + * @brief Reloads WDG's counter. + * + * @param[in] wdgp pointer to the @p WDGDriver object + * + * @notapi + */ +void wdg_lld_reset(WDGDriver * wdgp) { + + wdgp->wdg->KR = KR_KEY_RELOAD; +} + +#endif /* HAL_USE_WDG == TRUE */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.h b/os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.h new file mode 100644 index 000000000..056a451c6 --- /dev/null +++ b/os/hal/ports/STM32/LLD/xWDGv1/hal_wdg_lld.h @@ -0,0 +1,183 @@ +/* + ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +/** + * @file wdg_lld.h + * @brief WDG Driver subsystem low level driver header. + * + * @addtogroup WDG + * @{ + */ + +#ifndef _WDG_LLD_H_ +#define _WDG_LLD_H_ + +#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name RLR register definitions + * @{ + */ +#define STM32_IWDG_RL_MASK (0x00000FFF << 0) +#define STM32_IWDG_RL(n) ((n) << 0) +/** @} */ + +/** + * @name PR register definitions + * @{ + */ +#define STM32_IWDG_PR_MASK (7 << 0) +#define STM32_IWDG_PR_4 0U +#define STM32_IWDG_PR_8 1U +#define STM32_IWDG_PR_16 2U +#define STM32_IWDG_PR_32 3U +#define STM32_IWDG_PR_64 4U +#define STM32_IWDG_PR_128 5U +#define STM32_IWDG_PR_256 6U +/** @} */ + +/** + * @name WINR register definitions + * @{ + */ +#define STM32_IWDG_WIN_MASK (0x00000FFF << 0) +#define STM32_IWDG_WIN(n) ((n) << 0) +#define STM32_IWDG_WIN_DISABLED STM32_IWDG_WIN(0x00000FFF) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief IWDG driver enable switch. + * @details If set to @p TRUE the support for IWDG is included. + * @note The default is @p FALSE. + */ +#if !defined(STM32_WDG_USE_IWDG) || defined(__DOXYGEN__) +#define STM32_WDG_USE_IWDG FALSE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if STM32_WDG_USE_IWDG && !STM32_HAS_IWDG +#error "IWDG not present in the selected device" +#endif + +#if !STM32_WDG_USE_IWDG +#error "WDG driver activated but no xWDG peripheral assigned" +#endif + +#if !defined(STM32_LSI_ENABLED) +#error "STM32_LSI_ENABLED not defined" +#endif + +#if (STM32_WDG_USE_IWDG == TRUE) && (STM32_LSI_ENABLED == FALSE) +#error "IWDG requires LSI clock" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a structure representing an WDG driver. + */ +typedef struct WDGDriver WDGDriver; + +/** + * @brief Driver configuration structure. + * @note It could be empty on some architectures. + */ +typedef struct { + /** + * @brief Configuration of the IWDG_PR register. + * @details See the STM32 reference manual for details. + */ + uint32_t pr; + /** + * @brief Configuration of the IWDG_RLR register. + * @details See the STM32 reference manual for details. + */ + uint32_t rlr; +#if STM32_IWDG_IS_WINDOWED || defined(__DOXYGEN__) + /** + * @brief Configuration of the IWDG_WINR register. + * @details See the STM32 reference manual for details. + * @note This field is not present in F1, F2, F4, L1 sub-families. + */ + uint32_t winr; +#endif +} WDGConfig; + +/** + * @brief Structure representing an WDG driver. + */ +struct WDGDriver { + /** + * @brief Driver state. + */ + wdgstate_t state; + /** + * @brief Current configuration data. + */ + const WDGConfig *config; + /* End of the mandatory fields.*/ + /** + * @brief Pointer to the IWDG registers block. + */ + IWDG_TypeDef *wdg; +}; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if STM32_WDG_USE_IWDG && !defined(__DOXYGEN__) +extern WDGDriver WDGD1; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void wdg_lld_init(void); + void wdg_lld_start(WDGDriver *wdgp); + void wdg_lld_stop(WDGDriver *wdgp); + void wdg_lld_reset(WDGDriver *wdgp); +#ifdef __cplusplus +} +#endif + +#endif /* HAL_USE_WDG == TRUE */ + +#endif /* _WDG_LLD_H_ */ + +/** @} */ diff --git a/os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.c b/os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.c deleted file mode 100644 index 15cfefd72..000000000 --- a/os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file wdg_lld.c - * @brief WDG Driver subsystem low level driver source. - * - * @addtogroup WDG - * @{ - */ - -#include "hal.h" - -#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define KR_KEY_RELOAD 0xAAAAU -#define KR_KEY_ENABLE 0xCCCCU -#define KR_KEY_WRITE 0x5555U -#define KR_KEY_PROTECT 0x0000U - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -#if STM32_WDG_USE_IWDG || defined(__DOXYGEN__) -WDGDriver WDGD1; -#endif - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver interrupt handlers. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Low level WDG driver initialization. - * - * @notapi - */ -void wdg_lld_init(void) { - -#if STM32_WDG_USE_IWDG - WDGD1.state = WDG_STOP; - WDGD1.wdg = IWDG; -#endif -} - -/** - * @brief Configures and activates the WDG peripheral. - * - * @param[in] wdgp pointer to the @p WDGDriver object - * - * @notapi - */ -void wdg_lld_start(WDGDriver *wdgp) { - -#if STM32_IWDG_IS_WINDOWED - /* Enable IWDG and unlock for write.*/ - wdgp->wdg->KR = KR_KEY_ENABLE; - wdgp->wdg->KR = KR_KEY_WRITE; - - /* Write configuration.*/ - wdgp->wdg->PR = wdgp->config->pr; - wdgp->wdg->RLR = wdgp->config->rlr; - while (wdgp->wdg->SR != 0) - ; - - /* This also triggers a refresh.*/ - wdgp->wdg->WINR = wdgp->config->winr; -#else - /* Unlock IWDG.*/ - wdgp->wdg->KR = KR_KEY_WRITE; - - /* Write configuration.*/ - while (wdgp->wdg->SR != 0) - ; - wdgp->wdg->PR = wdgp->config->pr; - wdgp->wdg->RLR = wdgp->config->rlr; - - /* Start operations.*/ - wdgp->wdg->KR = KR_KEY_RELOAD; - wdgp->wdg->KR = KR_KEY_ENABLE; -#endif -} - -/** - * @brief Deactivates the WDG peripheral. - * - * @param[in] wdgp pointer to the @p WDGDriver object - * - * @api - */ -void wdg_lld_stop(WDGDriver *wdgp) { - - osalDbgAssert(wdgp->state == WDG_STOP, - "IWDG cannot be stopped once activated"); -} - -/** - * @brief Reloads WDG's counter. - * - * @param[in] wdgp pointer to the @p WDGDriver object - * - * @notapi - */ -void wdg_lld_reset(WDGDriver * wdgp) { - - wdgp->wdg->KR = KR_KEY_RELOAD; -} - -#endif /* HAL_USE_WDG == TRUE */ - -/** @} */ diff --git a/os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.h b/os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.h deleted file mode 100644 index 056a451c6..000000000 --- a/os/hal/ports/STM32/LLD/xWDGv1/wdg_lld.h +++ /dev/null @@ -1,183 +0,0 @@ -/* - ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -/** - * @file wdg_lld.h - * @brief WDG Driver subsystem low level driver header. - * - * @addtogroup WDG - * @{ - */ - -#ifndef _WDG_LLD_H_ -#define _WDG_LLD_H_ - -#if (HAL_USE_WDG == TRUE) || defined(__DOXYGEN__) - -/*===========================================================================*/ -/* Driver constants. */ -/*===========================================================================*/ - -/** - * @name RLR register definitions - * @{ - */ -#define STM32_IWDG_RL_MASK (0x00000FFF << 0) -#define STM32_IWDG_RL(n) ((n) << 0) -/** @} */ - -/** - * @name PR register definitions - * @{ - */ -#define STM32_IWDG_PR_MASK (7 << 0) -#define STM32_IWDG_PR_4 0U -#define STM32_IWDG_PR_8 1U -#define STM32_IWDG_PR_16 2U -#define STM32_IWDG_PR_32 3U -#define STM32_IWDG_PR_64 4U -#define STM32_IWDG_PR_128 5U -#define STM32_IWDG_PR_256 6U -/** @} */ - -/** - * @name WINR register definitions - * @{ - */ -#define STM32_IWDG_WIN_MASK (0x00000FFF << 0) -#define STM32_IWDG_WIN(n) ((n) << 0) -#define STM32_IWDG_WIN_DISABLED STM32_IWDG_WIN(0x00000FFF) -/** @} */ - -/*===========================================================================*/ -/* Driver pre-compile time settings. */ -/*===========================================================================*/ - -/** - * @name Configuration options - * @{ - */ -/** - * @brief IWDG driver enable switch. - * @details If set to @p TRUE the support for IWDG is included. - * @note The default is @p FALSE. - */ -#if !defined(STM32_WDG_USE_IWDG) || defined(__DOXYGEN__) -#define STM32_WDG_USE_IWDG FALSE -#endif -/** @} */ - -/*===========================================================================*/ -/* Derived constants and error checks. */ -/*===========================================================================*/ - -#if STM32_WDG_USE_IWDG && !STM32_HAS_IWDG -#error "IWDG not present in the selected device" -#endif - -#if !STM32_WDG_USE_IWDG -#error "WDG driver activated but no xWDG peripheral assigned" -#endif - -#if !defined(STM32_LSI_ENABLED) -#error "STM32_LSI_ENABLED not defined" -#endif - -#if (STM32_WDG_USE_IWDG == TRUE) && (STM32_LSI_ENABLED == FALSE) -#error "IWDG requires LSI clock" -#endif - -/*===========================================================================*/ -/* Driver data structures and types. */ -/*===========================================================================*/ - -/** - * @brief Type of a structure representing an WDG driver. - */ -typedef struct WDGDriver WDGDriver; - -/** - * @brief Driver configuration structure. - * @note It could be empty on some architectures. - */ -typedef struct { - /** - * @brief Configuration of the IWDG_PR register. - * @details See the STM32 reference manual for details. - */ - uint32_t pr; - /** - * @brief Configuration of the IWDG_RLR register. - * @details See the STM32 reference manual for details. - */ - uint32_t rlr; -#if STM32_IWDG_IS_WINDOWED || defined(__DOXYGEN__) - /** - * @brief Configuration of the IWDG_WINR register. - * @details See the STM32 reference manual for details. - * @note This field is not present in F1, F2, F4, L1 sub-families. - */ - uint32_t winr; -#endif -} WDGConfig; - -/** - * @brief Structure representing an WDG driver. - */ -struct WDGDriver { - /** - * @brief Driver state. - */ - wdgstate_t state; - /** - * @brief Current configuration data. - */ - const WDGConfig *config; - /* End of the mandatory fields.*/ - /** - * @brief Pointer to the IWDG registers block. - */ - IWDG_TypeDef *wdg; -}; - -/*===========================================================================*/ -/* Driver macros. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* External declarations. */ -/*===========================================================================*/ - -#if STM32_WDG_USE_IWDG && !defined(__DOXYGEN__) -extern WDGDriver WDGD1; -#endif - -#ifdef __cplusplus -extern "C" { -#endif - void wdg_lld_init(void); - void wdg_lld_start(WDGDriver *wdgp); - void wdg_lld_stop(WDGDriver *wdgp); - void wdg_lld_reset(WDGDriver *wdgp); -#ifdef __cplusplus -} -#endif - -#endif /* HAL_USE_WDG == TRUE */ - -#endif /* _WDG_LLD_H_ */ - -/** @} */ -- cgit v1.2.3