mohan-bee/mini-mp3-player
Portable, battery-powered MP3 player hardware with USB charging, microSD storage, an MCU, VS10xx audio decoder, headphone output, status controls, and regulated power rails.
- Version
- 1.2.0
- License
- unset
- Stars
- 0
firmware/src/board.c
#include "board.h"
#include "ch32v20x.h"
static bool adc_ready;
static void gpio(GPIO_TypeDef *port, uint16_t pins, GPIOMode_TypeDef mode) {
GPIO_InitTypeDef config = { .GPIO_Pin=pins, .GPIO_Speed=GPIO_Speed_50MHz, .GPIO_Mode=mode };
GPIO_Init(port, &config);
}
uint32_t millis(void) {
// Read the free-running 64-bit counter without a low-word rollover tear.
volatile uint32_t *counter = (volatile uint32_t *)&SysTick->CNT;
uint32_t high, low, check;
do { high=counter[1]; low=counter[0]; check=counter[1]; } while (high != check);
return (uint32_t)((((uint64_t)high << 32) | low) / (SystemCoreClock / 1000));
}
void delay_ms(uint32_t ms) { uint32_t start=millis(); while ((uint32_t)(millis()-start)<ms) {} }
void log_text(const char *s) {
while (*s) {
uint32_t start=millis();
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET)
if ((uint32_t)(millis()-start)>20) return;
USART_SendData(USART1, (uint8_t)*s++);
}
}
void log_number(uint32_t n) {
char text[11]; unsigned i=10; text[i]=0;
do { text[--i]=(char)('0'+n%10); n/=10; } while(n);
log_text(text+i);
}
void led_set(bool on) { GPIO_WriteBit(GPIOB, GPIO_Pin_11, on ? Bit_RESET : Bit_SET); }
uint8_t buttons_raw(void) {
uint8_t pressed=0;
if (!(GPIOA->INDR & GPIO_Pin_1)) pressed|=BUTTON_PLAY;
if (!(GPIOA->INDR & GPIO_Pin_2)) pressed|=BUTTON_PREV;
if (!(GPIOA->INDR & GPIO_Pin_3)) pressed|=BUTTON_NEXT;
if (!(GPIOB->INDR & GPIO_Pin_0)) pressed|=BUTTON_MODE;
return pressed;
}
static bool spi_byte(SPI_TypeDef *spi, uint8_t tx, uint8_t *rx) {
uint32_t start=millis();
while (!(spi->STATR & SPI_I2S_FLAG_TXE)) if ((uint32_t)(millis()-start)>10) return false;
SPI_I2S_SendData(spi,tx);
while (!(spi->STATR & SPI_I2S_FLAG_RXNE)) if ((uint32_t)(millis()-start)>10) return false;
*rx=(uint8_t)SPI_I2S_ReceiveData(spi);
while (spi->STATR & SPI_I2S_FLAG_BSY) if ((uint32_t)(millis()-start)>10) return false;
return true;
}
bool sd_byte(uint8_t tx, uint8_t *rx) { return spi_byte(SPI1,tx,rx); }
bool vs_byte(uint8_t tx, uint8_t *rx) { return spi_byte(SPI2,tx,rx); }
void sd_select(bool selected) { GPIO_WriteBit(GPIOA,GPIO_Pin_4,selected ? Bit_RESET : Bit_SET); }
void vs_select(bool data, bool selected) { GPIO_WriteBit(GPIOB,data ? GPIO_Pin_9 : GPIO_Pin_12,selected ? Bit_RESET : Bit_SET); }
void vs_reset(bool asserted) { GPIO_WriteBit(GPIOB,GPIO_Pin_8,asserted ? Bit_RESET : Bit_SET); }
bool vs_ready(void) { return (GPIOA->INDR & GPIO_Pin_8)!=0; }
static void spi_speed(SPI_TypeDef *spi,uint16_t prescaler) {
SPI_Cmd(spi,DISABLE);
spi->CTLR1=(spi->CTLR1 & ~SPI_BaudRatePrescaler_256)|prescaler;
SPI_Cmd(spi,ENABLE);
}
void sd_speed(bool fast) { spi_speed(SPI1,fast ? SPI_BaudRatePrescaler_8 : SPI_BaudRatePrescaler_256); }
void vs_speed(bool fast) { spi_speed(SPI2,fast ? SPI_BaudRatePrescaler_8 : SPI_BaudRatePrescaler_32); }
static bool adc_read(uint8_t channel,uint16_t *value) {
ADC_RegularChannelConfig(ADC1,channel,1,ADC_SampleTime_239Cycles5);
ADC_ClearFlag(ADC1,ADC_FLAG_EOC);
ADC_SoftwareStartConvCmd(ADC1,ENABLE);
uint32_t start=millis();
while (!ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)) if ((uint32_t)(millis()-start)>10) return false;
*value=ADC_GetConversionValue(ADC1); return true;
}
uint16_t battery_mv(void) {
if (!adc_ready) return UINT16_MAX;
uint32_t battery=0,reference=0;
for (unsigned i=0;i<16;i++) {
uint16_t b,r;
if (!adc_read(ADC_Channel_0,&b) || !adc_read(ADC_Channel_Vrefint,&r)) return UINT16_MAX;
battery+=b; reference+=r;
}
if (!reference) return UINT16_MAX;
// 100k/100k divider; nominal internal bandgap. Calibrate against a DMM.
return (uint16_t)(battery*2400u/reference);
}
void board_init(void) {
SystemCoreClockUpdate();
SysTick->CTLR=0; SysTick->CNT=0; SysTick->CMP=UINT64_MAX;
SysTick->CTLR=5; // Enable, HCLK, no interrupt, no automatic reload.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO|RCC_APB2Periph_SPI1|RCC_APB2Periph_USART1|RCC_APB2Periph_ADC1,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_SetBits(GPIOB,GPIO_Pin_9|GPIO_Pin_12|GPIO_Pin_11);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);
gpio(GPIOA,GPIO_Pin_4,GPIO_Mode_Out_PP);
gpio(GPIOB,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12|GPIO_Pin_11,GPIO_Mode_Out_PP);
gpio(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3,GPIO_Mode_IPU);
gpio(GPIOB,GPIO_Pin_0,GPIO_Mode_IPU);
gpio(GPIOA,GPIO_Pin_8|GPIO_Pin_6|GPIO_Pin_10,GPIO_Mode_IN_FLOATING);
gpio(GPIOB,GPIO_Pin_14,GPIO_Mode_IN_FLOATING);
gpio(GPIOA,GPIO_Pin_5|GPIO_Pin_7|GPIO_Pin_9,GPIO_Mode_AF_PP);
gpio(GPIOB,GPIO_Pin_13|GPIO_Pin_15,GPIO_Mode_AF_PP);
gpio(GPIOA,GPIO_Pin_0,GPIO_Mode_AIN);
SPI_InitTypeDef spi;
SPI_StructInit(&spi); spi.SPI_Mode=SPI_Mode_Master; spi.SPI_NSS=SPI_NSS_Soft;
spi.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_256;
SPI_Init(SPI1,&spi); SPI_Cmd(SPI1,ENABLE);
spi.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_32;
SPI_Init(SPI2,&spi); SPI_Cmd(SPI2,ENABLE);
USART_InitTypeDef uart; USART_StructInit(&uart); uart.USART_BaudRate=115200;
USART_Init(USART1,&uart); USART_Cmd(USART1,ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_InitTypeDef adc; ADC_StructInit(&adc); adc.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
adc.ADC_NbrOfChannel=1; ADC_Init(ADC1,&adc); ADC_BufferCmd(ADC1,DISABLE);
ADC_TempSensorVrefintCmd(ENABLE); ADC_Cmd(ADC1,ENABLE); delay_ms(2);
ADC_ResetCalibration(ADC1); uint32_t start=millis();
while (ADC_GetResetCalibrationStatus(ADC1)) if ((uint32_t)(millis()-start)>100) return;
ADC_StartCalibration(ADC1); start=millis();
while (ADC_GetCalibrationStatus(ADC1)) if ((uint32_t)(millis()-start)>100) return;
delay_ms(20);
adc_ready=true;
}