/* * This file is part of the usbcanrb project. * Copyright 2022 Edward V. Emelianov . * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "usb.h" #include "usbhw.h" #include "usb_lib.h" void USB_setup(){ RCC->APB1ENR |= RCC_APB1ENR_CRSEN | RCC_APB1ENR_USBEN; // enable CRS (hsi48 sync) & USB RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48 uint32_t tmout = 16000000; while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break;} FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; CRS->CFGR &= ~CRS_CFGR_SYNCSRC; CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only RCC->CFGR |= RCC_CFGR_SW; // allow RESET and WKUPM interrupts USB->CNTR = USB_CNTR_RESETM | USB_CNTR_WKUPM; // clear flags USB->ISTR = 0; // and activate pullup USB->BCDR |= USB_BCDR_DPPU; NVIC_EnableIRQ(USB_IRQn); } static uint16_t lastaddr = LASTADDR_DEFAULT; /** * Endpoint initialisation * !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!! * @param number - EP num (0...7) * @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT) * @param txsz - transmission buffer size @ USB/CAN buffer * @param rxsz - reception buffer size @ USB/CAN buffer * @param uint16_t (*func)(ep_t *ep) - EP handler function * @return 0 if all OK */ int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)()){ if(number >= STM32ENDPOINTS) return 4; // out of configured amount if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA); USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1; if(rxsz & 1 || rxsz > USB_BTABLE_SIZE) return 3; // wrong rx buffer size uint16_t countrx = 0; if(rxsz < 64) countrx = rxsz / 2; else{ if(rxsz & 0x1f) return 3; // should be multiple of 32 countrx = 31 + rxsz / 32; } USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr; endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr); endpoints[number].txbufsz = txsz; lastaddr += txsz; USB_BTABLE->EP[number].USB_COUNT_TX = 0; USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr; endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr); lastaddr += rxsz; // buffer size: Table127 of RM USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10; endpoints[number].func = func; return 0; } // standard IRQ handler void usb_isr(){ if (USB->ISTR & USB_ISTR_RESET){ // Reinit registers USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; USB->ISTR = 0; // Endpoint 0 - CONTROL // ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes! lastaddr = LASTADDR_DEFAULT; // roll back to beginning of buffer EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler); // clear address, leave only enable bit USB->DADDR = USB_DADDR_EF; // state is default - wait for enumeration USB_Dev.USB_Status = USB_STATE_DEFAULT; } if(USB->ISTR & USB_ISTR_CTR){ // EP number uint8_t n = USB->ISTR & USB_ISTR_EPID; // copy status register uint16_t epstatus = USB->EPnR[n]; // copy received bytes amount endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter // check direction if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit) if(n == 0){ // control endpoint if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack EP_Read(0, (uint8_t*)&setup_packet); // interrupt handler will be called later }else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf EP_Read(0, (uint8_t*)&ep0databuf); } } } // call EP handler if(endpoints[n].func) endpoints[n].func(endpoints[n]); } if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep usbON = 0; USB->CNTR |= USB_CNTR_FSUSP | USB_CNTR_LPMODE; USB->ISTR = ~USB_ISTR_SUSP; } if(USB->ISTR & USB_ISTR_WKUP){ // wakeup USB->CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LPMODE); // clear suspend flags USB->ISTR = ~USB_ISTR_WKUP; } }