2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
16 * author : emsr (shanggl@wo.cn)
17 * hw_config is more modified and is implemented using emsr's VCP code
18 * (implemented using timers)
29 #include "build/build_config.h"
31 #include "common/utils.h"
32 #include "drivers/io.h"
33 #include "build/atomic.h"
38 #include "cdc_class.h"
42 #include "drivers/time.h"
43 #include "at32f435_437_clock.h"
45 #include "serial_usb_vcp_at32f43x.h"
47 #include "at32f435_437_tmr.h"
50 otg_core_type otg_core_struct
;
51 #define USB_TIMEOUT 50
53 static vcpPort_t vcpPort
;
56 * @brief usb 48M clock select
57 * @param clk_s:USB_CLK_HICK, USB_CLK_HEXT
60 void usb_clock48m_select(usb_clk48_s clk_s
)
62 if(clk_s
== USB_CLK_HICK
)
64 crm_usb_clock_source_select(CRM_USB_CLOCK_SOURCE_HICK
);
66 /* enable the acc calibration ready interrupt */
67 crm_periph_clock_enable(CRM_ACC_PERIPH_CLOCK
, TRUE
);
69 /* update the c1\c2\c3 value */
74 acc_sof_select(ACC_SOF_OTG1
);
76 acc_sof_select(ACC_SOF_OTG2
);
78 /* open acc calibration */
79 acc_calibration_mode_enable(ACC_CAL_HICKTRIM
, TRUE
);
83 switch(system_core_clock
)
87 crm_usb_clock_div_set(CRM_USB_DIV_1
);
92 crm_usb_clock_div_set(CRM_USB_DIV_1_5
);
97 crm_usb_clock_div_set(CRM_USB_DIV_2
);
102 crm_usb_clock_div_set(CRM_USB_DIV_2_5
);
107 crm_usb_clock_div_set(CRM_USB_DIV_3
);
112 crm_usb_clock_div_set(CRM_USB_DIV_3_5
);
117 crm_usb_clock_div_set(CRM_USB_DIV_4
);
122 crm_usb_clock_div_set(CRM_USB_DIV_4_5
);
127 crm_usb_clock_div_set(CRM_USB_DIV_5
);
132 crm_usb_clock_div_set(CRM_USB_DIV_5_5
);
137 crm_usb_clock_div_set(CRM_USB_DIV_6
);
148 * @brief this function config gpio.
152 void usb_gpio_config(void)
154 gpio_init_type gpio_init_struct
;
156 crm_periph_clock_enable(OTG_PIN_GPIO_CLOCK
, TRUE
);
157 gpio_default_para_init(&gpio_init_struct
);
159 gpio_init_struct
.gpio_drive_strength
= GPIO_DRIVE_STRENGTH_STRONGER
;
160 gpio_init_struct
.gpio_out_type
= GPIO_OUTPUT_PUSH_PULL
;
161 gpio_init_struct
.gpio_mode
= GPIO_MODE_MUX
;
162 gpio_init_struct
.gpio_pull
= GPIO_PULL_NONE
;
165 gpio_init_struct
.gpio_pins
= OTG_PIN_DP
| OTG_PIN_DM
;
166 gpio_init(OTG_PIN_GPIO
, &gpio_init_struct
);
168 gpio_pin_mux_config(OTG_PIN_GPIO
, OTG_PIN_DP_SOURCE
, OTG_PIN_MUX
);
169 gpio_pin_mux_config(OTG_PIN_GPIO
, OTG_PIN_DM_SOURCE
, OTG_PIN_MUX
);
171 #ifdef USB_SOF_OUTPUT_ENABLE
172 crm_periph_clock_enable(OTG_PIN_SOF_GPIO_CLOCK
, TRUE
);
173 gpio_init_struct
.gpio_pins
= OTG_PIN_SOF
;
174 gpio_init(OTG_PIN_SOF_GPIO
, &gpio_init_struct
);
175 gpio_pin_mux_config(OTG_PIN_GPIO
, OTG_PIN_SOF_SOURCE
, OTG_PIN_MUX
);
178 /* otgfs use vbus pin */
179 #ifndef USB_VBUS_IGNORE
180 gpio_init_struct
.gpio_pins
= OTG_PIN_VBUS
;
181 gpio_init_struct
.gpio_pull
= GPIO_PULL_DOWN
;
182 gpio_pin_mux_config(OTG_PIN_GPIO
, OTG_PIN_VBUS_SOURCE
, OTG_PIN_MUX
);
183 gpio_init(OTG_PIN_GPIO
, &gpio_init_struct
);
188 #ifdef USB_LOW_POWER_WAKUP
190 * @brief usb low power wakeup interrupt config
194 void usb_low_power_wakeup_config(void)
196 exint_init_type exint_init_struct
;
198 crm_periph_clock_enable(CRM_SCFG_PERIPH_CLOCK
, TRUE
);
199 exint_default_para_init(&exint_init_struct
);
201 exint_init_struct
.line_enable
= TRUE
;
202 exint_init_struct
.line_mode
= EXINT_LINE_INTERRUPUT
;
203 exint_init_struct
.line_select
= OTG_WKUP_EXINT_LINE
;
204 exint_init_struct
.line_polarity
= EXINT_TRIGGER_RISING_EDGE
;
205 exint_init(&exint_init_struct
);
207 nvic_irq_enable(OTG_WKUP_IRQ
, NVIC_PRIO_USB_WUP
,0);
211 * @brief this function handles otgfs wakup interrupt.
215 void OTG_WKUP_HANDLER(void)
217 exint_flag_clear(OTG_WKUP_EXINT_LINE
);
223 /********************************************
227 uint32_t CDC_Send_FreeBytes(void)
230 return the bytes free in the circular buffer
232 functionally equivalent to:
233 (APP_Rx_ptr_out > APP_Rx_ptr_in ? APP_Rx_ptr_out - APP_Rx_ptr_in : APP_RX_DATA_SIZE - APP_Rx_ptr_in + APP_Rx_ptr_in)
234 but without the impact of the condition check.
237 ATOMIC_BLOCK(NVIC_PRIO_VCP
) {
238 freeBytes
= ((UserTxBufPtrOut
- UserTxBufPtrIn
) + (-((int)(UserTxBufPtrOut
<= UserTxBufPtrIn
)) & APP_TX_DATA_SIZE
)) - 1;
245 * @brief CDC_Send_DATA
246 * CDC received data to be send over USB IN endpoint are managed in
248 * @param ptrBuffer: Buffer of data to be sent
249 * @param sendLength: Number of data to be sent (in bytes)
252 uint32_t CDC_Send_DATA(const uint8_t *ptrBuffer
, uint32_t sendLength
)
254 for (uint32_t i
= 0; i
< sendLength
; i
++) {
255 while (CDC_Send_FreeBytes() == 0) {
256 // block until there is free space in the ring buffer
260 ATOMIC_BLOCK(NVIC_PRIO_VCP
) {
261 UserTxBuffer
[UserTxBufPtrIn
] = ptrBuffer
[i
];
262 UserTxBufPtrIn
= (UserTxBufPtrIn
+ 1) % APP_TX_DATA_SIZE
;
268 void TxTimerConfig(void){
269 /* Initialize TIMx peripheral as follow:
270 + Period = CDC_POLLING_INTERVAL*1000 - 1 every 5ms
271 + Prescaler = ((SystemCoreClock/2)/10000) - 1
273 + Counter direction = Up
275 crm_periph_clock_enable(CRM_TMR20_PERIPH_CLOCK
, TRUE
);
276 //timer, period, perscaler
277 tmr_base_init(usbTxTmr
,(CDC_POLLING_INTERVAL
- 1),((system_core_clock
)/10000 - 1));
278 //TMR_CLOCK_DIV1 = 0X00 NO DIV
279 tmr_clock_source_div_set(usbTxTmr
,TMR_CLOCK_DIV1
);
281 tmr_cnt_dir_set(usbTxTmr
,TMR_COUNT_UP
);
283 tmr_period_buffer_enable(usbTxTmr
,TRUE
);
285 tmr_interrupt_enable(usbTxTmr
, TMR_OVF_INT
, TRUE
);
287 nvic_irq_enable(TMR20_OVF_IRQn
,NVIC_PRIO_USB
,0);
289 tmr_counter_enable(usbTxTmr
,TRUE
);
294 * @brief TIM period elapsed callback
295 * @param htim: TIM handle
298 void TMR20_OVF_IRQHandler(void)
302 static uint32_t lastBuffsize
= 0;
304 cdc_struct_type
*pcdc
= (cdc_struct_type
*)otg_core_struct
.dev
.class_handler
->pdata
;
306 if (pcdc
->g_tx_completed
== 1) {
307 // endpoint has finished transmitting previous block
309 bool needZeroLengthPacket
= lastBuffsize
% 64 == 0;
311 // move the ring buffer tail based on the previous succesful transmission
312 UserTxBufPtrOut
+= lastBuffsize
;
313 if (UserTxBufPtrOut
== APP_TX_DATA_SIZE
) {
318 if (needZeroLengthPacket
) {
319 usb_vcp_send_data(&otg_core_struct
.dev
, (uint8_t*)&UserTxBuffer
[UserTxBufPtrOut
], 0);
323 if (UserTxBufPtrOut
!= UserTxBufPtrIn
) {
324 if (UserTxBufPtrOut
> UserTxBufPtrIn
) { /* Roll-back */
325 buffsize
= APP_TX_DATA_SIZE
- UserTxBufPtrOut
;
327 buffsize
= UserTxBufPtrIn
- UserTxBufPtrOut
;
329 if (buffsize
> APP_TX_BLOCK_SIZE
) {
330 buffsize
= APP_TX_BLOCK_SIZE
;
333 uint32_t txed
=usb_vcp_send_data(&otg_core_struct
.dev
,(uint8_t*)&UserTxBuffer
[UserTxBufPtrOut
], buffsize
);
335 lastBuffsize
= buffsize
;
339 tmr_flag_clear(usbTxTmr
,TMR_OVF_FLAG
);
342 /************************************************************/
344 uint8_t usbIsConnected(void){
345 return (USB_CONN_STATE_DEFAULT
!=otg_core_struct
.dev
.conn_state
);
348 uint8_t usbIsConfigured(void){
349 return (USB_CONN_STATE_CONFIGURED
==otg_core_struct
.dev
.conn_state
);
352 bool usbVcpIsConnected(const serialPort_t
*instance
)
355 return usbIsConnected() && usbIsConfigured();
359 * @brief this function handles otgfs interrupt.
363 void OTG_IRQ_HANDLER(void)
365 usbd_irq_handler(&otg_core_struct
);
369 static void usbVcpSetBaudRate(serialPort_t
*instance
, uint32_t baudRate
)
375 static void usbVcpSetMode(serialPort_t
*instance
, portMode_t mode
)
381 static bool isUsbVcpTransmitBufferEmpty(const serialPort_t
*instance
)
387 static uint32_t usbVcpAvailable(const serialPort_t
*instance
)
390 uint32_t available
=0;
392 available
=APP_Rx_ptr_in
-APP_Rx_ptr_out
;
394 // check anything that hasn't been copied into the cache
395 cdc_struct_type
*pcdc
= (cdc_struct_type
*)otg_core_struct
.dev
.class_handler
->pdata
;
396 if(pcdc
->g_rx_completed
== 1){
397 available
=pcdc
->g_rxlen
;
403 static uint8_t usbVcpRead(serialPort_t
*instance
)
407 // Check the cache is empty. If empty, add a read
408 if ((APP_Rx_ptr_in
==0)||(APP_Rx_ptr_out
== APP_Rx_ptr_in
)){
410 APP_Rx_ptr_in
=usb_vcp_get_rxdata(&otg_core_struct
.dev
,APP_Rx_Buffer
);// usb Maximum 64 bytes each time
413 // No data is read, return 0
417 return APP_Rx_Buffer
[APP_Rx_ptr_out
++];
420 // Write buffer data to vpc
421 static void usbVcpWriteBuf(serialPort_t
*instance
, const void *data
, int count
)
425 if (!usbVcpIsConnected(instance
)) {
429 uint32_t start
= millis();
430 const uint8_t *p
= data
;
432 uint32_t txed
= CDC_Send_DATA(p
, count
);
436 if (millis() - start
> USB_TIMEOUT
) {
442 static bool usbVcpFlush(vcpPort_t
*port
)
444 uint32_t count
= port
->txAt
;
451 if (!usbIsConnected() || !usbIsConfigured()) {
455 uint32_t start
= millis();
456 uint8_t *p
= port
->txBuf
;
458 uint32_t txed
= CDC_Send_DATA(p
, count
);
462 if (millis() - start
> USB_TIMEOUT
) {
469 static void usbVcpWrite(serialPort_t
*instance
, uint8_t c
)
471 vcpPort_t
*port
= container_of(instance
, vcpPort_t
, port
);
473 port
->txBuf
[port
->txAt
++] = c
;
474 if (!port
->buffering
|| port
->txAt
>= ARRAYLEN(port
->txBuf
)) {
479 static void usbVcpBeginWrite(serialPort_t
*instance
)
481 vcpPort_t
*port
= container_of(instance
, vcpPort_t
, port
);
482 port
->buffering
= true;
485 static uint32_t usbTxBytesFree(const serialPort_t
*instance
)
488 return CDC_Send_FreeBytes();
491 static void usbVcpEndWrite(serialPort_t
*instance
)
493 vcpPort_t
*port
= container_of(instance
, vcpPort_t
, port
);
494 port
->buffering
= false;
498 static const struct serialPortVTable usbVTable
[] = {
500 .serialWrite
= usbVcpWrite
,
501 .serialTotalRxWaiting
= usbVcpAvailable
,
502 .serialTotalTxFree
= usbTxBytesFree
,
503 .serialRead
= usbVcpRead
,
504 .serialSetBaudRate
= usbVcpSetBaudRate
,
505 .isSerialTransmitBufferEmpty
= isUsbVcpTransmitBufferEmpty
,
506 .setMode
= usbVcpSetMode
,
507 .isConnected
= usbVcpIsConnected
,
508 .writeBuf
= usbVcpWriteBuf
,
509 .beginWrite
= usbVcpBeginWrite
,
510 .endWrite
= usbVcpEndWrite
,
515 void usbVcpInitHardware(void)
517 IOInit(IOGetByTag(IO_TAG(PA11
)), OWNER_USB
, RESOURCE_INPUT
, 0);
518 IOInit(IOGetByTag(IO_TAG(PA12
)), OWNER_USB
, RESOURCE_OUTPUT
, 0);
520 /* usb gpio config */
523 #ifdef USB_LOW_POWER_WAKUP
524 usb_low_power_wakeup_config();
527 /* enable otgfs clock */
528 crm_periph_clock_enable(OTG_CLOCK
, TRUE
);
530 /* select usb 48m clcok source */
531 usb_clock48m_select(USB_CLK_HEXT
);
533 /* enable otgfs irq,cannot set too high priority */
534 nvic_irq_enable(OTG_IRQ
,NVIC_PRIO_USB
,0);
536 usbGenerateDisconnectPulse();
539 usbd_init(&otg_core_struct
,
540 USB_FULL_SPEED_CORE_ID
,
549 serialPort_t
*usbVcpOpen(void)
553 s
->port
.vTable
= usbVTable
;
555 return (serialPort_t
*)s
;
558 uint32_t usbVcpGetBaudRate(serialPort_t
*instance
)
561 cdc_struct_type
*pcdc
= (cdc_struct_type
*)otg_core_struct
.dev
.class_handler
->pdata
;
562 return pcdc
->linecoding
.bitrate
;
563 // return CDC_BaudRate();