Add basic thermal printer control

This commit is contained in:
fruchti 2018-07-29 18:24:52 +02:00
commit c695969e59
23 changed files with 1762 additions and 0 deletions

10
src/buildid.h Executable file
View file

@ -0,0 +1,10 @@
#ifndef BUILDID_H_
#define BUILDID_H_
extern char __BUILD_DATE;
extern char __BUILD_NUMBER;
#define BUILD_DATE ((uint32_t)&__BUILD_DATE)
#define BUILD_NUMBER ((uint32_t)&__BUILD_NUMBER)
#endif

29
src/debug.c Executable file
View file

@ -0,0 +1,29 @@
#include "debug.h"
void Debug_Print(const char *message)
{
uint32_t m[] = {2, (uint32_t)message, strlen(message)};
__asm__("mov r0, #0x05;"
"mov r1, %[m];"
"bkpt #0xAB"
:
: [m] "r" (m)
: "r0", "r1", "memory");
}
void Debug_PutChar(char c)
{
uint32_t m[] = {2, (uint32_t)(&c), 1};
__asm__("mov r0, #0x05;"
"mov r1, %[m];"
"bkpt #0xAB"
:
: [m] "r" (m)
: "r0", "r1", "memory");
// __asm__("mov r0, #0x03;"
// "mov r1, %[msg];"
// "bkpt #0xAB"
// :
// : [msg] "r" (&c)
// : "r0", "r1");
}

9
src/debug.h Executable file
View file

@ -0,0 +1,9 @@
#pragma once
#include <stdint.h>
#include <string.h>
#include "stm32f1xx.h"
void Debug_Print(const char *message);
void Debug_PutChar(char c);

423
src/ltp1245.c Normal file
View file

@ -0,0 +1,423 @@
#include "ltp1245.h"
#include "pinning.h"
#define LINEWIDTH (64 * 6)
uint8_t LTP1245_Buffer[LINEWIDTH / 8 * LTP1245_BUFFER_LINES];
typedef struct State_t
{
struct State_t (*fn)(void);
} State_t;
static volatile int Stepper_Delta = 0;
static int PrintLines;
static int CurrentBufferLine;
bool Printing = false;
static volatile int PulseWidth = 2000;
static void InitStepper(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
GPIOA->CRL = (GPIOA->CRL
& ~(0x0f << (4 * PIN_STEPPER_AM))
& ~(0x0f << (4 * PIN_STEPPER_AP))
& ~(0x0f << (4 * PIN_STEPPER_BM))
& ~(0x0f << (4 * PIN_STEPPER_BP)))
| (0x01 << (4 * PIN_STEPPER_AM)) // Output, max. 10 MHz
| (0x01 << (4 * PIN_STEPPER_AP)) // Output, max. 10 MHz
| (0x01 << (4 * PIN_STEPPER_BM)) // Output, max. 10 MHz
| (0x01 << (4 * PIN_STEPPER_BP)) // Output, max. 10 MHz
;
TIM3->PSC = 72000000 / 100 / LTP1245_MAX_DRIVE_FREQ - 1;
TIM3->ARR = 100;
TIM3->DIER = TIM_DIER_UIE;
TIM3->CR1 = TIM_CR1_CEN;
NVIC_EnableIRQ(TIM3_IRQn);
}
static void InitSensors(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
GPIOA->CRL = (GPIOA->CRL
& ~(0x0f << (4 * PIN_HEAD))
& ~(0x0f << (4 * PIN_PAPER)))
| (0x08 << (4 * PIN_HEAD)) // Input with pull-up/pull-down
| (0x08 << (4 * PIN_PAPER)) // Input with pull-up/pull-down
;
// Use pull-ups
GPIOA->BSRR = (1 << PIN_HEAD) | (1 << PIN_PAPER);
}
static void InitDataLines(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 719; // Each tick corresponds to ten microseconds
TIM2->ARR = 201; // 2 milliseconds
TIM2->CCR3 = 1;
TIM2->CCR4 = 1;
TIM2->CR1 = TIM_CR1_OPM; // One-pulse mode
TIM2->CCMR2 = TIM_CCMR2_OC3M // CH3 PWM mode 2
| TIM_CCMR2_OC4M // CH4 PWM mode 1
;
TIM2->CCER = 0;
TIM2->DIER = 0;
// Remap TIM2 lines
AFIO->MAPR |= AFIO_MAPR_TIM2_REMAP_FULLREMAP;
GPIOB->BRR = (1 << PIN_DST1) | (1 << PIN_DST2) | (1 << PIN_LATCH);
GPIOB->CRH = (GPIOB->CRH
& ~(0x0f << (4 * PIN_DST1 - 32))
& ~(0x0f << (4 * PIN_DST2 - 32))
& ~(0x0f << (4 * PIN_LATCH - 32))
& ~(0x0f << (4 * PIN_SCK - 32))
& ~(0x0f << (4 * PIN_DIN - 32)))
| (0x09 << (4 * PIN_DST1 - 32)) // Output in AF mode, max. 10 MHz
| (0x09 << (4 * PIN_DST2 - 32)) // Output in AF mode, max. 10 MHz
| (0x01 << (4 * PIN_LATCH - 32)) // Output, max. 10 MHz
| (0x09 << (4 * PIN_SCK - 32)) // Output in AF mode, max. 10 MHz
| (0x09 << (4 * PIN_DIN - 32)) // Output in AF mode, max. 10 MHz
;
SPI2->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SPE | SPI_CR1_MSTR
| SPI_CR1_BR_2;
}
static void InitThermistor(void)
{
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
GPIOB->CRL = (GPIOB->CRL
& ~(0x0f << PIN_THERMISTOR))
| (0x00 << PIN_THERMISTOR) // Analog mode
;
// Enable ADC
ADC1->CR2 |= ADC_CR2_ADON;
for(volatile int i = 0; i < 100; i++);
// Calibrate ADC
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
// Enable EOC interrupt
ADC1->CR1 = ADC_CR1_EOCIE;
NVIC_EnableIRQ(ADC1_2_IRQn);
// The thermistor is connected to ADC12_IN8 (PB0)
ADC1->SQR3 = (8 << ADC_SQR3_SQ1_Pos);
ADC1->SMPR2 = (7 << ADC_SMPR2_SMP8_Pos);
// Start conversion, continuous mode
ADC1->CR2 |= ADC_CR2_CONT;
ADC1->CR2 |= ADC_CR2_ADON;
}
static bool HasPaper(void)
{
return !(GPIOA->IDR & (1 << PIN_PAPER));
}
static bool HeadDown(void)
{
return !(GPIOA->IDR & (1 << PIN_HEAD));
}
void ActivateHead(int mask)
{
// Wait until timer has finished
while(TIM2->CR1 & TIM_CR1_CEN);
// Set activation pulse width
TIM2->ARR = (PulseWidth / 5) + 1;
TIM2->CCER = 0;
if(mask & 1)
{
TIM2->CCER |= TIM_CCER_CC4E;
}
if(mask & 2)
{
TIM2->CCER |= TIM_CCER_CC3E;
}
TIM2->CNT = 0;
TIM2->EGR = TIM_EGR_UG;
TIM2->CR1 |= TIM_CR1_CEN;
}
static void SendLine(uint8_t *line)
{
for(int i = 0; i < LINEWIDTH / 8; i++)
{
while(~SPI2->SR & SPI_SR_TXE);
*((volatile uint8_t*)(&SPI2->DR)) = line[i];
}
while(SPI2->SR & SPI_SR_BSY);
GPIOB->BSRR = (1 << PIN_LATCH);
for(volatile int i = 0; i < 1000; i++);
GPIOB->BRR = (1 << PIN_LATCH);
}
// Main state machine states
static State_t State_Idle(void);
static State_t State_PaperLoad(void);
static State_t State_InitialPaperFeed(void);
static State_t State_Printing(void);
static State_t State_FinalPaperFeed(void);
// Automatic paper loading
static State_t State_PaperLoad(void)
{
if(!HasPaper())
{
if(HeadDown())
{
Stepper_Delta = 0;
}
else if(Stepper_Delta < 1000)
{
Stepper_Delta = 1000;
}
}
else
{
if(Stepper_Delta == 0)
{
return (State_t){State_Idle};
}
}
return (State_t){State_PaperLoad};
}
// Paper feed before printing
static State_t State_InitialPaperFeed(void)
{
if(Stepper_Delta == 0)
{
return (State_t){State_Printing};
}
return (State_t){State_InitialPaperFeed};
}
// Actual printing
static State_t State_Printing(void)
{
if(!HeadDown() || !HasPaper())
{
Printing = false;
return (State_t){State_Idle};
}
if(Stepper_Delta == 1)
{
ActivateHead(3);
}
else if(Stepper_Delta == 0)
{
SendLine(LTP1245_Buffer + CurrentBufferLine * LINEWIDTH / 8);
ActivateHead(3);
CurrentBufferLine++;
if(CurrentBufferLine >= LTP1245_BUFFER_LINES)
{
CurrentBufferLine = 0;
}
PrintLines--;
if(PrintLines > 0)
{
Stepper_Delta = 2;
}
else
{
Stepper_Delta = 500;
return (State_t){State_FinalPaperFeed};
}
}
return (State_t){State_Printing};
}
// Paper feed after printing
static State_t State_FinalPaperFeed(void)
{
if(!HasPaper() || !HeadDown())
{
Printing = false;
return (State_t){State_Idle};
}
if(Stepper_Delta == 0)
{
Printing = false;
return (State_t){State_Idle};
}
return (State_t){State_FinalPaperFeed};
}
static State_t State_Idle(void)
{
if(!HasPaper())
{
return (State_t){State_PaperLoad};
}
if(Printing)
{
Stepper_Delta = 200;
return (State_t){State_InitialPaperFeed};
}
return (State_t){State_Idle};
}
void LTP1245_Print(void)
{
PrintLines = LTP1245_BUFFER_LINES * 20;
CurrentBufferLine = 0;
Printing = true;
}
void LTP1245_Init(void)
{
InitDataLines();
InitSensors();
InitStepper();
InitThermistor();
for(int i = 0; i < sizeof(LTP1245_Buffer); i++)
{
int shift = (i * 8 / (64 * 6)) % 8;
LTP1245_Buffer[i] =(0x11 << shift) | (0x11 >> (8 - shift));
}
LTP1245_Print();
}
void AdvanceStateMachine(void)
{
static State_t state = {State_Idle};
state = state.fn();
}
void TIM3_IRQHandler(void)
{
if(TIM3->SR & TIM_SR_UIF)
{
static int substep = 0;
static bool off;
const int TABLE[] = {0, 1, 3, 2};
const int GPIO_MASK = ((1 << PIN_STEPPER_AM) | (1 << PIN_STEPPER_AP)
| (1 << PIN_STEPPER_BM) | (1 << PIN_STEPPER_BP));
if(Stepper_Delta != 0)
{
off = false;
if(Stepper_Delta > 0)
{
substep++;
if(substep > 3)
substep = 0;
Stepper_Delta--;
}
else
{
substep--;
if(substep < 0)
substep = 3;
Stepper_Delta++;
}
GPIOA->ODR = (GPIOA->ODR & ~GPIO_MASK)
| ((TABLE[substep] & 1) ? (1 << PIN_STEPPER_AP)
: (1 << PIN_STEPPER_AM))
| ((TABLE[substep] & 2) ? (1 << PIN_STEPPER_BP)
: (1 << PIN_STEPPER_BM));
}
else
{
if(off)
{
GPIOA->ODR = (GPIOA->ODR & ~GPIO_MASK);
substep = 0;
}
off = true;
}
AdvanceStateMachine();
TIM3->SR &= ~TIM_SR_UIF;
}
}
void ADC1_2_IRQHandler(void)
{
const int READINGS[] =
{
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 375.54)), // -40 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 275.39)), // -35 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 204.55)), // -30 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 153.76)), // -25 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 116.89)), // -20 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 89.82)), // -15 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 69.71)), // -10 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 54.61)), // -5 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 43.17)), // 0 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 34.42)), // 5 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 27.66)), // 10 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 22.40)), // 15 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 18.27)), // 20 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 15.00)), // 25 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 12.40)), // 30 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 10.31)), // 35 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 8.63)), // 40 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 7.26)), // 45 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 6.14)), // 50 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 5.22)), // 55 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 4.46)), // 60 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 3.83)), // 65 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 3.30)), // 70 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 2.86)), // 75 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 2.48)), // 80 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 2.17)), // 85 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 1.90)), // 90 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 1.67)), // 95 °C
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 1.47)) // 100 °C
};
int adc = ADC1->DR;
// Find first temperature higher than the measured one
int lower_entry = 0;
for(int i = 1; i < sizeof(READINGS) / sizeof(READINGS[0]); i++)
{
if(adc >= READINGS[i])
{
lower_entry = i - 1;
break;
}
}
int higher_entry = lower_entry + 1;
int temp = lower_entry * 5 - 40; // Temperature in °C
// Interpolate linearly
if(higher_entry < sizeof(READINGS) / sizeof(READINGS[0]))
{
int diff = READINGS[lower_entry] - READINGS[higher_entry];
temp += (READINGS[lower_entry] - adc) * 5 / diff;
}
// Use the formula from section 3.6, adjusted for integer arithmetic and
// a pulse with in microseconds
PulseWidth = (285 * 178 - (int)(1000 * 178 * 0.003135) * (temp - 25))
/ (int)((5 * 1.4 - 2.9) * (5 * 1.4 - 2.9));
}

14
src/ltp1245.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include <stdbool.h>
#include "stm32f1xx.h"
typedef int (*LTP1245_DataProvider_t)(uint8_t *buffer);
#define LTP1245_MAX_DRIVE_FREQ 473 // In Hz
#define LTP1245_BUFFER_LINES 64
#define LTP1245_TH_REXT 10 // In kΩ
void LTP1245_Init(void);

12
src/main.c Executable file
View file

@ -0,0 +1,12 @@
#include "main.h"
int main(void)
{
USB_Init();
LTP1245_Init();
for(;;)
{
}
}

15
src/main.h Executable file
View file

@ -0,0 +1,15 @@
#ifndef MAIN_H_
#define MAIN_H_
#include <stdbool.h>
#include "stm32f1xx.h"
#include "pinning.h"
#include "buildid.h"
#include "debug.h"
#include "usb.h"
#include "ltp1245.h"
int main(void);
#endif

23
src/pinning.h Executable file
View file

@ -0,0 +1,23 @@
#pragma once
// Port A
#define PIN_STEPPER_AP 0 // PA0 - Stepper phase A positive
#define PIN_STEPPER_BP 1 // PA1 - Stepper phase B positive
#define PIN_HEAD 2 // PA2 - Head up sensor
#define PIN_PAPER 3 // PA3 - Paper detect
#define PIN_STEPPER_AM 4 // PA4 - Stepper phase A negative
#define PIN_STEPPER_BM 5 // PA5 - Stepper phase B negative
#define PIN_USB_DM 11 // PA11 - USB D-
#define PIN_USB_DP 12 // PA12 - USB D+
// Port B
#define PIN_THERMISTOR 0 // PB0 - Thermistor
#define PIN_DST1 10 // PB10 - Head drive signal 1 (TIM2_CH3)
#define PIN_DST2 11 // PB11 - Head drive signal 2 (TIM2_CH4)
#define PIN_SCK 13 // PB13 - Thermal printer SCK (SPI2)
#define PIN_LATCH 14 // PB14 - Thermal printer data latch
#define PIN_DIN 15 // PB15 - Thermal printer MOSI (SPI2)
// Port C
#define PIN_LED 13 // PC13 - Status LED

32
src/system.c Executable file
View file

@ -0,0 +1,32 @@
#include <stdint.h>
#include "stm32f103x6.h"
void SystemInit(void)
{
// Activate HSE and wait for it to be ready
RCC->CR = RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
RCC->CFGR = RCC_CFGR_SW_0;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_0);
FLASH->ACR |= FLASH_ACR_LATENCY_1;
// Set PLL to x9 (-> 72MHz system clock)
RCC->CFGR |= RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC | RCC_CFGR_PPRE1_2;
// Activate PLL and wait
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
// Select PLL as clock source
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_1;
// Resulting clocks:
// SYSCLK, AHB, APB2 72 Mhz
// APB1, ADC 36 MHz
// Disable all interrupts
RCC->CIR = 0x00000000;
}

376
src/usb.c Executable file
View file

@ -0,0 +1,376 @@
#include "usb.h"
#include "usb_descriptors.h"
#include "usb_com.h"
uint8_t USB_DeviceStatus[2] = {0x00, 0x01};
volatile unsigned int USB_ResetCount = 0;
volatile unsigned int USB_Address = 0;
static inline void USB_SetEPR(volatile uint16_t *EPR, uint16_t status)
{
// Caution: This function does a read-modify-write and is prone to
// unexpected behaviour when there are transactions going one, because the
// register contents might change during the funciton's execution. Thus, only
// use this function in initialisation code!
volatile uint16_t v = *EPR;
status ^= v & (USB_EP0R_DTOG_RX | USB_EP0R_STAT_RX |\
USB_EP0R_DTOG_TX | USB_EP0R_STAT_TX);
*EPR = status;
}
static inline void USB_SetEPRXStatus(volatile uint16_t *EPR, uint16_t status)
{
uint16_t v = *EPR;
v ^= status & USB_EP0R_STAT_RX;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA | USB_EP0R_STAT_RX;
*EPR = v;
}
static inline void USB_SetEPTXStatus(volatile uint16_t *EPR, uint16_t status)
{
uint16_t v = *EPR;
v ^= status & USB_EP0R_STAT_TX;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA | USB_EP0R_STAT_TX;
*EPR = v;
}
static inline void USB_SetEPType(volatile uint16_t *EPR, uint16_t type)
{
uint16_t v = *EPR;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA;
v |= USB_EP0R_EP_TYPE & type;
*EPR = v;
}
static inline void USB_SetEPAddress(volatile uint16_t *EPR, uint16_t address)
{
uint16_t v = *EPR;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX;
v |= USB_EP0R_EA & address;
*EPR = v;
}
static inline void USB_SetEPKind(volatile uint16_t *EPR)
{
uint16_t v = *EPR;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_CTR_TX | USB_EP0R_EA;
v |= USB_EP0R_EP_KIND;
*EPR = v;
}
static inline void USB_ClearEPKind(volatile uint16_t *EPR)
{
uint16_t v = *EPR;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_CTR_TX | USB_EP0R_EA;
*EPR = v;
}
static inline void USB_ClearEPCTRX(volatile uint16_t *EPR)
{
uint16_t v = *EPR;
v &= USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA;
*EPR = v;
}
static inline void USB_ClearEPCTTX(volatile uint16_t *EPR)
{
uint16_t v = *EPR;
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA;
*EPR = v;
}
static inline void USB_Delay(unsigned int delay)
{
SysTick->LOAD = delay;
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
while(!((SysTick->CTRL) & SysTick_CTRL_COUNTFLAG_Msk));
SysTick->CTRL = 0;
}
void USB_Init(void)
{
// GPIOA clock
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
// for(int i = 0; i < 8; i++)
// {
// USB_BTABLE_ENTRIES[i].ADDR_RX = 0;
// USB_BTABLE_ENTRIES[i].ADDR_TX = 0;
// USB_BTABLE_ENTRIES[i].COUNT_RX = 0;
// USB_BTABLE_ENTRIES[i].COUNT_TX = 0;
// }
GPIOA->CRH &= ~(GPIO_CRH_CNF11 | GPIO_CRH_MODE11 |\
GPIO_CRH_CNF12 | GPIO_CRH_MODE12);
GPIOA->CRH |= GPIO_CRH_MODE11 | GPIO_CRH_MODE12;
GPIOA->ODR &= ~(GPIO_CRH_MODE11 | GPIO_CRH_MODE12);
USB_Delay(100000);
// Enable reset and correct transfer interrupts
NVIC_EnableIRQ(USB_LP_IRQn);
// Analog power up
USB->CNTR = (uint16_t)USB_CNTR_FRES;
// Minimum delay: 1 µs
USB_Delay(3000);
USB->CNTR = (uint16_t)0;
USB->ISTR = (uint16_t)0;
USB->CNTR = (uint16_t)(USB_CNTR_RESETM | USB_CNTR_CTRM);
// Configure USB pins (PA11 and PA12 in AF mode, 50 MHz push-pull)
GPIOA->CRH |= GPIO_CRH_CNF11_1 | GPIO_CRH_MODE11 |\
GPIO_CRH_CNF12_1 | GPIO_CRH_MODE12;
}
static inline void USB_HandleReset(void)
{
// Remove reset flag
USB->ISTR = (uint16_t)~(USB_ISTR_RESET);
USB_ResetCount++;
// Set buffer table origin
USB->BTABLE = USB_BTABLE_OFFSET;
// Set buffer table contents for control endpoint 0
USB_BTABLE_ENTRIES[0].COUNT_RX = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
// USB_BTABLE_ENTRIES[0].COUNT_RX = (4 << 10);
USB_BTABLE_ENTRIES[0].ADDR_RX = 0x40;
USB_BTABLE_ENTRIES[0].COUNT_TX = 0;
USB_BTABLE_ENTRIES[0].ADDR_TX = 0x80;
// Set buffer table contents for control endpoint 1
USB_BTABLE_ENTRIES[1].COUNT_RX_0 = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
USB_BTABLE_ENTRIES[1].ADDR_RX_0 = 0x100;
USB_BTABLE_ENTRIES[1].COUNT_RX_1 = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
USB_BTABLE_ENTRIES[1].ADDR_RX_1 = 0x140;
// Configure endpoint 0
USB_SetEPR(&(USB->EP0R), USB_EPR_EP_TYPE_CONTROL |\
USB_EPR_STAT_TX_NAK | USB_EPR_STAT_RX_VALID);
// Configure endpoint 1
USB_SetEPR(&(USB->EP1R), USB_EPR_EP_TYPE_ISO |\
USB_EPR_STAT_TX_DISABLED | USB_EPR_STAT_RX_VALID |\
(1 << USB_EP0R_EA_Pos));
// Enable
USB->DADDR = USB_DADDR_EF;
}
static inline void USB_PMAToMemory(uint8_t *mem, uint16_t offset, size_t length)
{
uint8_t *pma = (uint8_t*)(USB_PMA_ADDR + 2 * offset);
for(unsigned int i = 0; i < length / 2; i++)
{
mem[2 * i] = *pma++;
mem[2 * i + 1] = *pma++;
pma += 2;
}
}
static inline void USB_MemoryToPMA(uint16_t offset, const uint8_t *mem, size_t length)
{
uint16_t *pma = (uint16_t*)(USB_PMA_ADDR + 2 * offset);
for(unsigned int i = 0; i < length / 2; i++)
{
uint16_t tmp = mem[2 * i] | (mem[2 * i + 1] << 8);
*pma++ = tmp;
pma++;
}
}
static inline void USB_HandleIn(void)
{
if((USB->DADDR & USB_DADDR_ADD) != USB_Address)
{
USB->DADDR = USB_Address | USB_DADDR_EF;
}
// Ready for next packet
USB_SetEPRXStatus(&USB->EP0R, USB_EP_RX_VALID);
}
static inline void USB_HandleOut(void)
{
// Ready for next packet
USB_SetEPRXStatus(&USB->EP0R, USB_EP_RX_VALID);
}
static inline void USB_HandleSetup(void)
{
USB_SetupPacket_t sp;
//memcpy(&sp, (const void*)(USB_PMA_ADDR + USB_BTABLE_ENTRIES[0].ADDR_RX), sizeof(USB_SetupPacket_t));
USB_PMAToMemory((uint8_t*)&sp, USB_BTABLE_ENTRIES[0].ADDR_RX, sizeof(USB_SetupPacket_t));
const uint8_t *reply_data = NULL;
int reply_length = 0;
uint8_t reply_response = USB_TOKEN_ACK;
if((sp.bmRequestType & (USB_REQUEST_TYPE | USB_REQUEST_RECIPIENT)) \
== (USB_REQUEST_TYPE_STANDARD | USB_REQUEST_RECIPIENT_DEVICE))
{
switch(sp.bRequest)
{
case USB_REQUEST_GET_STATUS:;
if(sp.wValue == 0 && sp.wIndex == 0 && sp.wLength == 2)
{
reply_length = 2;
reply_data = USB_DeviceStatus;
}
break;
case USB_REQUEST_GET_DESCRIPTOR:;
USB_DescriptorType_t descriptor_type = sp.wValue >> 8;
int descriptor_index = sp.wValue & 0xff;
switch(descriptor_type)
{
case USB_DEVICE_DESCRIPTOR:;
reply_data = USB_DeviceDescriptor.raw;
reply_length = USB_DeviceDescriptor.bLength;
break;
case USB_CONFIGURATION_DESCRIPTOR:;
reply_data = USB_ConfigurationInterfaceDescriptor.raw;
if(sp.wLength < USB_ConfigurationInterfaceDescriptor.configuration.wTotalLength)
{
reply_length = USB_ConfigurationInterfaceDescriptor.configuration.bLength;
}
else
{
reply_length = USB_ConfigurationInterfaceDescriptor.configuration.wTotalLength;
}
break;
case USB_STRING_DESCRIPTOR:;
switch(descriptor_index)
{
case 0:;
reply_data = (uint8_t*)USB_StringDescriptor_LangID;
reply_length = (uint8_t)*USB_StringDescriptor_LangID;
break;
case 1:;
reply_data = (uint8_t*)USB_StringDescriptor_Vendor;
reply_length = (uint8_t)*USB_StringDescriptor_Vendor;
break;
case 2:;
reply_data = (uint8_t*)USB_StringDescriptor_Product;
reply_length = (uint8_t)*USB_StringDescriptor_Product;
break;
case 3:;
reply_data = (uint8_t*)USB_StringDescriptor_Serial;
reply_length = (uint8_t)*USB_StringDescriptor_Serial;
break;
default:;
__asm__ volatile("bkpt");
}
break;
case USB_INTERFACE_DESCRIPTOR:;
reply_data = USB_ConfigurationInterfaceDescriptor.interface0.raw;
reply_length = USB_ConfigurationInterfaceDescriptor.interface0.bLength;
break;
case USB_ENDPOINT_DESCRIPTOR:;
// Does not exist yet
__asm__ volatile("bkpt");
break;
}
break;
case USB_REQUEST_SET_ADDRESS:
USB_Address = sp.wValue & USB_DADDR_ADD;
reply_response = USB_EP_TX_VALID;
break;
case USB_REQUEST_SET_CONFIGURATION:
reply_response = USB_EP_TX_VALID;
break;
default:;
reply_response = USB_EP_TX_STALL;
break;
}
}
else
{
reply_length = USBCOM_HandleSetupPacket(&sp, &reply_data);
}
if(reply_data)
{
// Reply with data
//memcpy((void*)(USB_PMA_ADDR + USB_BTABLE_ENTRIES[0].ADDR_TX), reply_data, reply_length);
USB_MemoryToPMA(USB_BTABLE_ENTRIES[0].ADDR_TX, reply_data, reply_length);
USB_BTABLE_ENTRIES[0].COUNT_TX = reply_length;
USB_SetEPTXStatus(&(USB->EP0R), USB_EP_TX_VALID);
}
else
{
// Send response
USB_BTABLE_ENTRIES[0].COUNT_TX = 0;
USB_SetEPTXStatus(&(USB->EP0R), reply_response);
}
}
// Interrupt service routine
void USB_LP_IRQHandler(void)
{
if(USB->ISTR & USB_ISTR_RESET)
{
// Reset happened
USB_HandleReset();
return;
}
uint16_t istr;
while((istr = USB->ISTR) & (USB_ISTR_CTR))
{
if(istr & USB_ISTR_CTR)
{
// USB->ISTR = (uint16_t)~USB_ISTR_CTR;
// Correct transfer
int ep = istr & USB_ISTR_EP_ID;
if(ep == 0)
{
if(istr & USB_ISTR_DIR)
{
// OUT transfer
if(USB->EP0R & USB_EP0R_SETUP)
{
// Clear CTR_RX
USB->EP0R = USB->EP0R & (USB_EP0R_SETUP | USB_EP0R_EP_TYPE |\
USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA);
// Setup packed received
USB_HandleSetup();
}
else
{
// Clear CTR_RX
USB->EP0R = USB->EP0R & (USB_EP0R_SETUP | USB_EP0R_EP_TYPE |\
USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA);
USB_HandleOut();
}
}
else
{
// Clear CTR_TX
USB->EP0R = USB->EP0R & (USB_EP0R_CTR_RX | USB_EP0R_SETUP |\
USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA);
// IN transfer
USB_HandleIn();
}
}
else if(ep == 1)
{
USBCOM_HandleISO0OUT();
// Ready for next packet
// USB_SetEPRXStatus(&USB->EP1R, USB_EP_RX_VALID);
}
}
}
}

113
src/usb.h Executable file
View file

@ -0,0 +1,113 @@
#pragma once
#include <string.h>
#include "stm32f1xx.h"
#include "pinning.h"
#include "debug.h"
#define USB_TOKEN_OUT 0b0001
#define USB_TOKEN_IN 0x1001
#define USB_TOKEN_SOF 0b0101
#define USB_TOKEN_SETUP 0b1101
#define USB_TOKEN_DATA0 0b0011
#define USB_TOKEN_DATA1 0b1011
#define USB_TOKEN_DATA2 0b0111
#define USB_TOKEN_ACK 0b0010
#define USB_TOKEN_NAK 0b1010
#define USB_TOKEN_STALL 0b1110
#define USB_TOKEN_NYET 0b0110
#define USB_TOKEN_PRE 0b1100
#define USB_TOKEN_ERR 0b1100
#define USB_TOKEN_SPLIT 0b1000
#define USB_TOKEN_PING 0b0100
#define USB_REQUEST_DIRECTION (1 << 7)
#define USB_REQUEST_DIRECTION_OUT 0
#define USB_REQUEST_DIRECTION_IN (1 << 7)
#define USB_REQUEST_TYPE (0x3 << 5)
#define USB_REQUEST_TYPE_STANDARD 0
#define USB_REQUEST_TYPE_CLASS (1 << 5)
#define UBS_REQUEST_TYPE_VENDOR (2 << 5)
#define UBS_REQUEST_TYPE_RESERVED (3 << 5)
#define USB_REQUEST_RECIPIENT 0x1f
#define USB_REQUEST_RECIPIENT_DEVICE 0
#define USB_REQUEST_RECIPIENT_INTERFACE 1
#define USB_REQUEST_RECIPIENT_ENDPOINT 2
#define USB_REQUEST_RECIPIENT_OTHER 3
#define USB_REQUEST_CLEAR_FEATURE 1 // wValue = <feature>, wIndex = 0|<interface>|<endpoint>, wLength = 0
#define USB_REQUEST_GET_CONFIGURATION 8 // wValue = 0, wIndex = 0, wLength = 1
#define USB_REQUEST_GET_DESCRIPTOR 6 // wValue = <descriptor type>:<descriptor index>, wIndex = 0|<language>, wLength = <descriptor length>
#define USB_REQUEST_GET_INTERFACE 10 // wValue = 0, wIndex = <interface>, wLength = 1
#define USB_REQUEST_GET_STATUS 0 // wValue = 0, wIndex = 0|<interface>|<endpoint>, wLength = 2
#define USB_REQUEST_SET_ADDRESS 5 // wValue = <address>, wIndex = 0, wLength = 0
#define USB_REQUEST_SET_CONFIGURATION 9 // wValue = <configuration value>, wIndex = 0, wLength = 0
#define USB_REQUEST_SET_DESCRIPTOR 7 // wValue = <descriptor type>:<descriptor index>, wIndex = 0|<language>, wLength = <descriptor length>
#define USB_REQUEST_SET_FEATURE 3 // wValue = <feature selector>, wIndex = 0|<interface>|<endpoint>, wLength = 0
#define USB_REQUEST_SET_INTERFACE 11 // wValue = <alternate setting>, wIndex = <interface>, wLength = 0
#define USB_REQUEST_SYNCH_FRAME 12 // wValue = 0, wIndex = <endpoint>, wLength = 2
#define USB_EPR_STAT_TX_DISABLED 0x00
#define USB_EPR_STAT_TX_STALL USB_EP0R_STAT_TX_0
#define USB_EPR_STAT_TX_NAK USB_EP0R_STAT_TX_1
#define USB_EPR_STAT_TX_VALID (USB_EP0R_STAT_TX_0 | USB_EP0R_STAT_TX_1)
#define USB_EPR_STAT_RX_DISABLED 0x00
#define USB_EPR_STAT_RX_STALL USB_EP0R_STAT_RX_0
#define USB_EPR_STAT_RX_NAK USB_EP0R_STAT_RX_1
#define USB_EPR_STAT_RX_VALID (USB_EP0R_STAT_RX_0 | USB_EP0R_STAT_RX_1)
#define USB_EPR_EP_TYPE_BULK 0x00
#define USB_EPR_EP_TYPE_CONTROL USB_EP0R_EP_TYPE_0
#define USB_EPR_EP_TYPE_ISO USB_EP0R_EP_TYPE_1
#define USB_EPR_EP_TYPE_INTERRUPT (USB_EP0R_EP_TYPE_0 | USB_EP0R_EP_TYPE_1)
#define USB_PMA_ADDR 0x40006000UL
#define USB_BTABLE_OFFSET 0x00
#define USB_EP_RXCOUNT_BL_SIZE (1 << 15)
typedef struct
{
volatile union
{
volatile uint16_t ADDR_TX;
volatile uint16_t ADDR_RX_0;
volatile uint16_t ADDR_TX_0;
};
volatile uint16_t rsvd1;
volatile union
{
volatile uint16_t COUNT_TX;
volatile uint16_t COUNT_RX_0;
volatile uint16_t COUNT_TX_0;
};
volatile uint16_t rsvd2;
volatile union
{
volatile uint16_t ADDR_RX;
volatile uint16_t ADDR_RX_1;
volatile uint16_t ADDR_TX_1;
};
volatile uint16_t rsvd3;
volatile union
{
volatile uint16_t COUNT_RX;
volatile uint16_t COUNT_RX_1;
volatile uint16_t COUNT_TX_1;
};
volatile uint16_t rsvd4;
} __attribute__((packed, aligned(4))) USB_BufferTableEntry_t;
typedef struct
{
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__((packed, aligned(2))) USB_SetupPacket_t;
#define USB_BTABLE_ENTRIES ((USB_BufferTableEntry_t*)(USB_PMA_ADDR + USB_BTABLE_OFFSET))
void USB_Init(void);

33
src/usb_com.c Executable file
View file

@ -0,0 +1,33 @@
#include "usb_com.h"
unsigned USBCOM_Registers[USBCOM_N_REGISTERS];
uint8_t USBCOM_HandleSetupPacket(USB_SetupPacket_t *sp, const uint8_t **reply_data)
{
uint8_t reply_length = 0;
if(sp->bmRequestType & USB_REQUEST_DIRECTION_IN)
{
// Read register
if(sp->bRequest < USBCOM_N_REGISTERS)
{
*reply_data = (uint8_t*)&USBCOM_Registers[sp->bRequest];
reply_length = sp->wLength;
}
}
else
{
// Write register
if(sp->bRequest < USBCOM_N_REGISTERS)
{
USBCOM_Registers[sp->bRequest] = ((uint32_t)(sp->wIndex) << 16) | sp->wValue;
}
}
return reply_length;
}
void USBCOM_HandleISO0OUT(void)
{
__asm__("bkpt");
}

14
src/usb_com.h Executable file
View file

@ -0,0 +1,14 @@
#pragma once
#include "usb.h"
typedef enum {
USBCOM_REG_LEDCOUNT,
USBCOM_N_REGISTERS
} USBCOM_Register_t;
extern unsigned USBCOM_Registers[USBCOM_N_REGISTERS];
uint8_t USBCOM_HandleSetupPacket(USB_SetupPacket_t *sp, const uint8_t **reply_data);
void USBCOM_HandleISO0OUT(void);

75
src/usb_descriptors.c Executable file
View file

@ -0,0 +1,75 @@
#include "usb_descriptors.h"
const USB_DeviceDescriptor_t USB_DeviceDescriptor =
{
.bLength = 18,
.bDescriptorType = USB_DEVICE_DESCRIPTOR,
.bcdUSB = 0x0200,
.bDeviceClass = 0xff,
.bDeviceSubClass = 0xff,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 64,
.idVendor = 0x16c0,
.idProduct = 0x05dc,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1
};
const USB_WholeDescriptor_t USB_ConfigurationInterfaceDescriptor =
{
.configuration = (USB_ConfigurationDescriptor_t)
{
.bLength = 9,
.bDescriptorType = USB_CONFIGURATION_DESCRIPTOR,
.wTotalLength = sizeof(USB_WholeDescriptor_t),
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 100
},
.interface0 = (USB_InterfaceDescriptor_t)
{
.bLength = 9,
.bDescriptorType = USB_INTERFACE_DESCRIPTOR,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0
},
.endpoint0out = (USB_EndpointDescriptor_t)
{
.bLength = sizeof(USB_EndpointDescriptor_t),
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
.bEndpointAddress = 1,
.bmAttributes = 1,
.wMaxPacketSize = 64,
.bInterval = 1
}
};
#define USB_STRING_LANGID 0x0409
#define USB_STRING_VENDOR 'F', 'r', 'u', 'c', 'h', 't', \
'i', '\'', 's', 0
#define USB_STRING_PRODUCT 'e', 'x', 't', 'r', 'e', 'm', \
'e', 'l', 'y', ' ', \
'd', 'u', 'm', 'b', ' ', \
't', 'h', 'e', 'r', 'm', 'a', \
'l', ' ', 'p', 'r', 'i', 'n', \
't', 'e', 'r', 0
#define USB_STRING_SERIAL 'v', '0', '.', '0', '0', '1', 0
const uint16_t USB_StringDescriptor_LangID[] =
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_LANGID);
const uint16_t USB_StringDescriptor_Vendor[] =
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_VENDOR);
const uint16_t USB_StringDescriptor_Product[] =
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_PRODUCT);
const uint16_t USB_StringDescriptor_Serial[] =
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_SERIAL);

106
src/usb_descriptors.h Executable file
View file

@ -0,0 +1,106 @@
#pragma once
#include <stdint.h>
typedef union
{
struct
{
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
};
uint8_t raw[18];
} __attribute__((packed)) USB_DeviceDescriptor_t;
typedef union
{
struct
{
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __attribute__((packed, aligned(1)));
uint8_t raw[9];
} __attribute__((packed, aligned(1))) USB_ConfigurationDescriptor_t;
typedef union
{
struct
{
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} __attribute__((packed, aligned(1)));
uint8_t raw[9];
} __attribute__((packed, aligned(1))) USB_InterfaceDescriptor_t;
typedef union
{
struct
{
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
};
uint8_t raw[7];
} __attribute__((packed)) USB_EndpointDescriptor_t;
typedef union
{
struct
{
USB_ConfigurationDescriptor_t configuration;
USB_InterfaceDescriptor_t interface0;
USB_EndpointDescriptor_t endpoint0out;
} __attribute__((packed, aligned(1)));
uint8_t raw[18];
} __attribute__((packed, aligned(1))) USB_WholeDescriptor_t;
typedef enum
{
USB_DEVICE_DESCRIPTOR = 1,
USB_CONFIGURATION_DESCRIPTOR,
USB_STRING_DESCRIPTOR,
USB_INTERFACE_DESCRIPTOR,
USB_ENDPOINT_DESCRIPTOR
} __attribute__((packed)) USB_DescriptorType_t;
#define USB_STRING_DESCRIPTOR_LENGTH(...) (sizeof((uint16_t[]){__VA_ARGS__}) + 2)
#define USB_BUILD_STRING_DESCRIPTOR(...) {USB_STRING_DESCRIPTOR_LENGTH(__VA_ARGS__) | (USB_STRING_DESCRIPTOR << 8), __VA_ARGS__}
extern const uint16_t USB_StringDescriptor_LangID[];
extern const uint16_t USB_StringDescriptor_Vendor[];
extern const uint16_t USB_StringDescriptor_Product[];
extern const uint16_t USB_StringDescriptor_Serial[];
extern const USB_DeviceDescriptor_t USB_DeviceDescriptor;
// extern const USB_ConfigurationDescriptor_t USB_ConfigurationDescriptor;
// extern const USB_InterfaceDescriptor_t USB_InterfaceDescriptor;
extern const USB_WholeDescriptor_t USB_ConfigurationInterfaceDescriptor;