Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.cpp
- Committer:
- peu605
- Date:
- 2017-09-07
- Revision:
- 8:3e913d0c7b67
- Parent:
- 7:f3536af611e6
- Child:
- 9:c2ff4ebc4327
File content as of revision 8:3e913d0c7b67:
#include "dit88prostm32.h"
#include "mbed.h"
/**
* STM32F103C8T6 Blue Pill, Low Layer drivers test
*
* Software Digital audio Transmitter
* for Roland SC-88pro (32kHz, 18bit, Right justified, 256fs)
*
* @author masuda, Masuda Naika
*/
#define _SPI SPI2
#define _SPIRxDMA DMA1
#define _SPIRxDMACh LL_DMA_CHANNEL_4
#define _SPIRxDMAIRQn DMA1_Channel4_IRQn
#define _SPITxDMA DMA1
#define _SPITxDMACh LL_DMA_CHANNEL_5
#define _SPI_NSS_SOFTWARE() _SPI->CR1 |= SPI_CR1_SSM // NSS softare management
#define _SPI_DESELECT_SLAVE() _SPI->CR1 |= SPI_CR1_SSI // deselect slave
#define _SPI_SELECT_SLAVE() _SPI->CR1 &= ~SPI_CR1_SSI // select slave
#define _SPI_ENABLE() _SPI->CR1 |= SPI_CR1_SPE // SPI enable
#define _SPI_DISABLE() _SPI->CR1 &= ~SPI_CR1_SPE // SPI disable
#define _LED_ON() LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_13)
#define _LED_OFF() LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_13)
#define BUFFER_NUM_FRAMES 4 // LR|LR/LR|LR, IRQ freq is 16kHz
#define DMA_TRANSFER_LENGTH (8 * 2 * BUFFER_NUM_FRAMES)
// serial audio data length
#define SDATA_LENGH 18
#define MAX_SPI_DELAY 13 // 32 - 18 -1
#define MASK31_20 0xfff00000
#define MASK23_0 0x00ffffff
//#define _DEBUG_CONSOLE
// SPI-DMA buffers
volatile SpiRxBuff spiRxBuff[BUFFER_NUM_FRAMES];
volatile SpiTxBuff spiTxBuff[BUFFER_NUM_FRAMES];
// channel status table
volatile ChannelStatus channelStatus[24];
// SPI bit delay
volatile uint32_t spiDelay = 0;
// Serial pc(PA_2, PA_3);
extern "C" void DMA1_Channel4_IRQHandler(void)
{
// SPI Rx DMA
if(LL_DMA_IsActiveFlag_HT4(_SPIRxDMA)) {
// half transfer
LL_DMA_ClearFlag_HT4(_SPIRxDMA);
transferFrames();
} else if (LL_DMA_IsActiveFlag_TC4(_SPIRxDMA)) {
// transfer complete
LL_DMA_ClearFlag_TC4(_SPIRxDMA);
transferFrames();
}
}
int main()
{
// change to 72MHz
SystemClock_Config();
setupPeripherals();
setChannelStatus();
HAL_Delay(1000);
// enable SPI and deselect
_SPI_DESELECT_SLAVE();
_SPI_ENABLE(); // LL_SPI_Enable(_SPI);
uint16_t spiCR1 = _SPI->CR1 & ~SPI_CR1_SSI; // use pre calculated value
// LRCK Hi = Left channel
// Wait LRCK rise and start SPI as soon as possible
while(LL_GPIO_IsInputPinSet(GPIOB, LL_GPIO_PIN_12) != 0); // wait for falling edge
while(LL_GPIO_IsInputPinSet(GPIOB, LL_GPIO_PIN_12) == 0); // wait for rising edge
// select slave
_SPI->CR1 = spiCR1;
// wait for interrupt
while (true) {
__WFI();
#ifdef _DEBUG_CONSOLE
debugOut();
#endif
}
}
void transferFrames()
{
// measured load is 18.6% at 72MHz
_LED_OFF(); // PC_13 to HIGH
for (uint32_t i = 0; i < BUFFER_NUM_FRAMES / 2; ++i) {
transferFrame();
}
_LED_ON(); // PC_13 to LOW
}
void transferFrame()
{
static uint32_t frameIndex = 0;
static uint32_t buffIndex = 0;
uint16_t *rxBuffPtr, *txBuffPtr;
// transfer left channel
rxBuffPtr = (uint16_t*) &spiRxBuff[buffIndex].ltCh;
txBuffPtr = (uint16_t*) &spiTxBuff[buffIndex].aCh;
transferSubFrame(frameIndex, true, rxBuffPtr, txBuffPtr);
// transfer right channel
rxBuffPtr = (uint16_t*) &spiRxBuff[buffIndex].rtCh;
txBuffPtr = (uint16_t*) &spiTxBuff[buffIndex].bCh;
transferSubFrame(frameIndex, false, rxBuffPtr, txBuffPtr);
if (++frameIndex == 192) {
frameIndex = 0;
}
if (++buffIndex == BUFFER_NUM_FRAMES) {
buffIndex = 0;
}
}
void transferSubFrame(uint32_t frameIndex, bool aCh, uint16_t *rxBuffPtr, uint16_t *txBuffPtr)
{
static bool lastCellZero = true;
// static uint32_t spiDelay = 0;
// ======================================
// Read rx buffer and make raw SPIDF data
// ======================================
// Roland SC-88pro, 256fs, 18bit right justified, MSB first
// SC-88pro's output format seems to be 2's complement 18 bit audio data in 20 bit data frame.
//
// SPIRxBuff, each channel data have 128(=256/2) bits, M:MSB, L:LSB
// 127 <--- ---> 0
// 0000 ... 0000000 00000000000M==== ===============L
// 1111 ... 1111111 11111111111M==== ===============L
// ---- ... ------- ---------------- ----------------
// +0 -> 5 hlf_word +6 +7
// 1111 ... 0000000 1111110000000000 1111110000000000
// 5432 6543210 5432109876543210 5432109876543210
//
// Actually, SPI starts some SCKs after LRCK change.
// Received data are shifted leftward.
//
// 127 <--- ---> 0
// |<->| spiDelay
// M========= ==========L
// ---- ... ------- ---------------- ----------------
// +0 -> 5 hlf_word +6 +7
// 1111 ... 0000000 1111110000000000 1111110000000000
// 5432 6543210 5432109876543210 5432109876543210
//
// bit[127:20] is all 0 or all 1. SDO keeps last LSB state.
//
// bit[31:20] should be 000...00 or 111...11
// bit[19:0] MSB to LSB, 2's complement 18 bit audio data in 20 bit data frame
//
// measured spiDelay is 2 to 4
// arithmetic right shift
int32_t val = (*(rxBuffPtr + 6) << 16) | *(rxBuffPtr + 7);
val >>= spiDelay;
// determine spiDelay
while(true) {
int32_t test = val & MASK31_20;
if (test == 0 || test == MASK31_20) {
break;
} else {
if (spiDelay == MAX_SPI_DELAY) {
spiDelay = 0;
val = 0; // discard data
break;
} else {
++spiDelay;
val >>= 1;
}
}
}
// left shift to fit 24 bit width, adding zero
val <<= (24 - SDATA_LENGH);
// 4-27 data, 28 V, 29 U, 30 C, 31 P
// U = 0
// 'val' here does not have preamble.
// So, V position is (28 - 4), C pos is (30 - 4), and P pos is (31 - 4).
// mask with 24 bit to clear VUCP bits
val &= MASK23_0;
// set Channel status bit
uint32_t bitPos = frameIndex & 7;
uint32_t offset = frameIndex >> 3;
uint8_t *chStatusPtr = aCh
? (uint8_t*) &channelStatus[offset].ltCh
: (uint8_t*) &channelStatus[offset].rtCh;
if (*chStatusPtr & _BV(bitPos)) {
val |= _BV(30 - 4);
}
// set parity bit
if (oddParity(val)) {
val |= _BV(31 - 4);
}
// ===============================================
// Biphase Mark Code encode and write to Tx buffer
// ===============================================
// Z:preamble, A:aux, L:LSB, M:MSB, 0:filler(unused bit)
// V:validity, U:user, C:ch status, P:parity
// ===> BMC encode from LSB to MSB
//(PCUVM================L00AAAAZZZZ)
// PCUVM================L00AAAA w/o preamble (= val)
// --------------------------------
// 33222222222211111111110000000000
// 10987654321098765432109876543210
// Write Biphase Mark Code data to SPITx-DMA buffer
// First, 0 ~ 4 bit is Preamble. Not BMC!
uint16_t bmc;
if (!aCh) {
// b channel
bmc = lastCellZero ? PREAMBLE_Y : ~PREAMBLE_Y;
} else if (frameIndex == 0) {
// a channel, frame 0
bmc = lastCellZero ? PREAMBLE_Z : ~PREAMBLE_Z;
} else {
// a channel, frame != 0
bmc = lastCellZero ? PREAMBLE_X : ~PREAMBLE_X;
}
// lastCellZero = !(bmc & 1);
lastCellZero = (bmc & 1) == 0;
*txBuffPtr++ = bmc;
// Next, 5 ~ 31 bit, audio data and VUCP bits. 28/4 = 7 loops
// BMC encode each by lower 4 bits
for (uint32_t i = 0; i < 7 ; ++i) {
bmc = lastCellZero
? bmcTable[val & 0x0f] : ~bmcTable[val & 0x0f];
// lastCellZero = !(bmc & 1);
lastCellZero = (bmc & 1) == 0;
*txBuffPtr++ = bmc;
val >>= 4;
}
}
// consumer, copy allowed, 18bit, 32kHz
void setChannelStatus()
{
// Byte 0: General control and mode information
// copy enable
channelStatus[0].ltCh = _BV(C_COPY);
channelStatus[0].rtCh = _BV(C_COPY);
// Byte 1: Category code
// general
channelStatus[1].ltCh = 0;
channelStatus[1].rtCh = 0;
// Byte 2: Source and channel number
channelStatus[2].ltCh = 0b0001 << 4;
channelStatus[2].rtCh = 0b0010 << 4;
// Byte 3: Sampling frequency and clock accuracy
channelStatus[3].ltCh = _BV(C_FS1) | _BV(C_FS0);
channelStatus[3].rtCh = _BV(C_FS1) | _BV(C_FS0);
// Byte 23: CRC
uint8_t *chStatusPtr = (uint8_t*) channelStatus;
channelStatus[23].ltCh = calcCRC(chStatusPtr);
++chStatusPtr;
channelStatus[23].rtCh = calcCRC(chStatusPtr);
}
uint32_t oddParity(uint32_t val)
{
val ^= val >> 16;
val ^= val >> 8;
val ^= val >> 4;
val ^= val >> 2;
val ^= val >> 1;
return val & 1;
}
uint8_t calcCRC(uint8_t *chStatusPtr)
{
uint8_t crc = 255;
for (uint32_t i = 0; i < 24; ++i) {
uint8_t b = *chStatusPtr;
for (uint32_t j = 0; j < 8; ++j) {
if ((crc & 1) ^ (b & 1)) {
crc >>= 1;
crc ^= 0xb8;
} else {
crc >>= 1;
}
b >>= 1;
}
chStatusPtr += 2;
}
return crc;
}
#ifdef _DEBUG_CONSOLE
void debugOut()
{
char str[32];
// printf("SystemCoreClock =%d\n", SystemCoreClock);
// // dump raw data
// uint16_t tmp = spiRxBuff[0].ltCh[6];
// toBinary(&tmp, (char*) &str);
// printf("Delay:%02d %s ", spiDelay, str);
// tmp = spiRxBuff[0].ltCh[7];
// toBinary(&tmp, (char*) &str);
// printf("%s\n", str);
// HAL_Delay(200);
// dump shifted data
int32_t val = (spiRxBuff[0].ltCh[6] << 16) | (spiRxBuff[0].ltCh[7]);
val >>= spiDelay;
uint16_t tmp = (val & 0xffff0000) >> 16;
toBinary(&tmp, (char*) &str);
printf("Delay:%02d %s ", spiDelay, str);
tmp = val & 0xffff;
toBinary(&tmp, (char*) &str);
printf("%s\n", str);
HAL_Delay(200);
// // find loud 18bit data
// int32_t val = (spiRxBuff[0].ltCh[6] << 16) | (spiRxBuff[0].ltCh[7]);
// val >>= spiDelay;
// uint16_t tmp = (val & 0xffff0000) >> 16;
// if ((tmp & 0b1111) == 0b1110 || (tmp & 0b1111) == 0b0001) {
// toBinary(&tmp, (char*) &str);
// printf("Delay:%02d %s ", spiDelay, str);
// tmp = val & 0xffff;
// toBinary(&tmp, (char*) &str);
// printf("%s\n", str);
// }
}
void toBinary(uint16_t *val_ptr, char *str)
{
uint16_t val = *val_ptr;
for (uint32_t j = 0; j < 16; ++j) {
*str = val & 0x8000 ? '1' : '0';
val <<= 1;
++str;
}
*str = '\0';
}
#endif
/**
* @brief System Clock Configuration
* The system Clock is configured as follow :
* System Clock source = PLL (HSE)
* SYSCLK(Hz) = 72000000
* HCLK(Hz) = 72000000
* AHB Prescaler = 1
* APB1 Prescaler = 2
* APB2 Prescaler = 1
* HSE Frequency(Hz) = 8000000
* PLLMUL = 9
* Flash Latency(WS) = 2
* @param None
* @retval None
*/
void SystemClock_Config(void)
{
HAL_RCC_DeInit();
// LL_RCC_DeInit(); // not implemented yet?
// /* Set FLASH latency */
// LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
//
// /* Enable HSE oscillator */
// LL_RCC_HSE_Enable();
// while(LL_RCC_HSE_IsReady() != 1) {
// };
//
// /* Main PLL configuration and activation */
// LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_9);
//
// LL_RCC_PLL_Enable();
// while(LL_RCC_PLL_IsReady() != 1) {
// };
//
// /* Sysclk activation on the main PLL */
// LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
// LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
// while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
// };
//
// /* Set APB1 & APB2 prescaler*/
// LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_2);
// LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
//
// /* Set systick to 1ms in using frequency set to 72MHz */
// LL_Init1msTick(72000000);
//
// /* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
// LL_SetSystemCoreClock(72000000);
SetSysClock_PLL_HSE(false); // "system_clock.c", 'bypass = false' means using X'tal.
SystemCoreClockUpdate(); // "system_stm32f1xx.h"
// restart uart
extern serial_t stdio_uart;
serial_baud(&stdio_uart, 9600);
}
void setupPeripherals()
{
// enable GPIOB/C clock
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOC);
// Configure GPIO pin : LED_Pin, PC13, OUTPUT
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_13, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_13, LL_GPIO_OUTPUT_PUSHPULL);
LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_13, LL_GPIO_SPEED_FREQ_MEDIUM);
// SPI IO pins
// PB13 -> SCK, 5V tolerant
LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_13, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_13, LL_GPIO_PULL_DOWN);
LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_13, LL_GPIO_SPEED_FREQ_MEDIUM);
// PB14 -> MISO, 5V tolerant
LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_14, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_14, LL_GPIO_PULL_DOWN);
LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_14, LL_GPIO_SPEED_FREQ_MEDIUM);
// PB15 -> MOSI, 5V tolerant
LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_15, LL_GPIO_MODE_ALTERNATE);
LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_15, LL_GPIO_PULL_DOWN);
LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_15, LL_GPIO_SPEED_FREQ_MEDIUM);
// PB12 -> LRCK, 5V tolerant
LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_12, LL_GPIO_MODE_INPUT);
// LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_12, LL_GPIO_PULL_DOWN);
LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_12, LL_GPIO_SPEED_FREQ_MEDIUM);
// SPI Slave full duplex, mode 0, 16bit
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); // SPI2
LL_SPI_SetMode(_SPI, LL_SPI_MODE_SLAVE);
LL_SPI_SetTransferDirection(_SPI,LL_SPI_FULL_DUPLEX);
LL_SPI_SetClockPhase(_SPI, LL_SPI_PHASE_1EDGE);
LL_SPI_SetClockPolarity(_SPI, LL_SPI_POLARITY_LOW);
LL_SPI_SetTransferBitOrder(_SPI, LL_SPI_MSB_FIRST);
LL_SPI_SetDataWidth(_SPI, LL_SPI_DATAWIDTH_16BIT);
LL_SPI_SetNSSMode(_SPI, LL_SPI_NSS_SOFT);
// connect to DMAReq
LL_SPI_EnableDMAReq_RX(_SPI);
LL_SPI_EnableDMAReq_TX(_SPI);
// DMA
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1); // DMA1
// _SPIRxDMA, DMA1 Channel 4
LL_DMA_ConfigTransfer(_SPIRxDMA, _SPIRxDMACh,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_PRIORITY_HIGH | LL_DMA_MODE_CIRCULAR |
LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT |
LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD);
LL_DMA_ConfigAddresses(_SPIRxDMA, _SPIRxDMACh,
(uint32_t)&(_SPI->DR), (uint32_t)&spiRxBuff,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
LL_DMA_SetDataLength(_SPIRxDMA, _SPIRxDMACh, DMA_TRANSFER_LENGTH);
// _SPITxDMA, DMA1 Channel5
LL_DMA_ConfigTransfer(_SPITxDMA, _SPITxDMACh,
LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_PRIORITY_MEDIUM | LL_DMA_MODE_CIRCULAR |
LL_DMA_PERIPH_NOINCREMENT | LL_DMA_MEMORY_INCREMENT |
LL_DMA_PDATAALIGN_HALFWORD | LL_DMA_MDATAALIGN_HALFWORD);
LL_DMA_ConfigAddresses(_SPITxDMA, _SPITxDMACh,
(uint32_t)&spiTxBuff, (uint32_t)&(_SPI->DR),
LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
LL_DMA_SetDataLength(_SPITxDMA, _SPITxDMACh, DMA_TRANSFER_LENGTH);
// Enable SPIRx DMA IRQ, Half Transfer and Transfer Complete
NVIC_SetPriority(_SPIRxDMAIRQn, 0);
NVIC_EnableIRQ(_SPIRxDMAIRQn);
LL_DMA_EnableIT_TC(_SPIRxDMA, _SPIRxDMACh);
LL_DMA_EnableIT_HT(_SPIRxDMA, _SPIRxDMACh);
// Enable DMA Channel
LL_DMA_EnableChannel(_SPIRxDMA, _SPIRxDMACh);
LL_DMA_EnableChannel(_SPITxDMA, _SPITxDMACh);
}
/*
// Use java to create BMC table, because I am a javaer.
private void start() {
StringBuilder sb = new StringBuilder();
for (char i = 0; i < 16; ++i) {
sb.append(String.format("%02d", (int) i)).append(" = 0b");
boolean lastCellZero = true;
char c = i;
for (int j = 0; j < 4; ++j) {
if ((c & 1) == 0) {
if (lastCellZero) {
sb.append("1111");
lastCellZero = false;
} else {
sb.append("0000");
lastCellZero = true;
}
} else {
if (lastCellZero) {
sb.append("1100");
lastCellZero = true;
} else {
sb.append("0011");
lastCellZero = false;
}
}
c >>= 1;
}
sb.append('\n');
}
System.out.println(sb.toString());
}
*/