#include #include "microcobs.h" #include "crc32.h" static uint8_t crc8_calc(uint8_t *data, size_t len); static bool parity_calc(uint8_t *data, size_t len); uint32_t mems_trx_word(uint32_t data); uint32_t mems_trx_cmd(uint32_t cmd); void mems_write_reg(int addr, int val); uint32_t mems_read_reg(int addr); int16_t mems_read_meas(int ch); void mems_spi_init(void); void mems_init(void); struct __attribute__((packed)) ll_pkt_trailer { uint32_t crc32; }; struct __attribute__((packed)) req_pkt { uint32_t req_seq; struct ll_pkt_trailer trailer; }; struct __attribute__((packed)) res_pkt { uint32_t res_seq; uint16_t meas_data[16]; struct ll_pkt_trailer trailer; }; struct tx_state { uint8_t *tx_char; int remaining_bytes; }; static crc32_t pkt_crc(void *pkt, struct ll_pkt_trailer *trailer); crc32_t pkt_crc(void *pkt, struct ll_pkt_trailer *trailer) { crc32_t crc = crc32_reset(); for (uint8_t *in = (uint8_t *)pkt; in < (uint8_t *)trailer; in++) { crc = crc32_update(crc, *in); } return crc32_finalize(crc); } static void packetize(void *pkt, struct ll_pkt_trailer *trailer); void packetize(void *pkt, struct ll_pkt_trailer *trailer) { trailer->crc32 = pkt_crc(pkt, trailer); } enum mems_regs { MEMS_REG_CTRL0, /* 0 */ MEMS_REG_CTRL1, /* 1 */ MEMS_REG_CONFIG, /* 2 */ MEMS_REG_STATUS0, /* 3 */ MEMS_REG_STATUS1, /* 4 */ MEMS_REG_STATUS2, /* 5 */ MEMS_REG_CHIP_REVID, /* 6 */ MEMS_REG_ACC_CHX_LOW, /* 7 */ MEMS_REG_ACC_CHX_HIGH, /* 8 */ MEMS_REG_ACC_CHY_LOW, /* 9 */ MEMS_REG_ACC_CHY_HIGH, /* 10 */ MEMS_REG_OSC_COUNTER, /* 11 */ MEMS_REG_ID_SENSOR_TYPE, /* 12 */ MEMS_REG_ID_VEH_MANUF, /* 13 */ MEMS_REG_ID_SENSOR_MANUF, /* 14 */ MEMS_REG_ID_LOT0, /* 15 */ MEMS_REG_ID_LOT1, /* 16 */ MEMS_REG_ID_LOT2, /* 17 */ MEMS_REG_ID_LOT3, /* 18 */ MEMS_REG_ID_WAFER, /* 19 */ MEMS_REG_ID_COOR_X, /* 20 */ MEMS_REG_ID_COOR_Y, /* 21 */ MEMS_REG_RESET, /* 22 */ MEMS_REG_OFF_CHX_HIGH, /* 23 */ MEMS_REG_OFF_CHX_LOW, /* 24 */ MEMS_REG_OFF_CHY_HIGH, /* 25 */ MEMS_REG_OFF_CHY_LOW, /* 26 */ }; uint8_t crc8_calc(uint8_t *data, size_t len) { int acc = 0; for (size_t i=0; i>= 1; } } return acc; } uint32_t mems_trx_word(uint32_t data) { /* CAUTION: ST's SPI peripherals behave differently depending on DR register access size, yet the CMSIS headers * expose it as an 32-bit uint only. In this case, we actually want a 32-bit access. */ uint16_t *dr = (uint16_t *)&SPI1->DR; *dr = data>>16; while (SPI1->SR & SPI_SR_BSY) ; uint32_t out = (*dr) << 16; *dr = data&0xffff; while (SPI1->SR & SPI_SR_BSY) ; out |= *dr; return out; } uint32_t mems_trx_cmd(uint32_t cmd) { GPIOA->BRR = 1<<15; /* De-assert !CS */ uint8_t bytes[3] = {(cmd>>16)&0xff, (cmd>>8)&0xff, cmd&0xff}; uint8_t crc = crc8_calc(bytes, 3); int parity = !!parity_calc(bytes, 3); uint32_t out = mems_trx_word(cmd | (parity<BSRR = 1<<15; /* Assert !CS */ return out; } void mems_write_reg(int addr, int val) { addr &= MEMS_ADDR_Msk; val &= MEMS_DATA_Msk; (void)mems_trx_cmd((1<> MEMS_DATA_Pos) & MEMS_DATA_Msk; } int16_t mems_read_meas(int ch) { ch &= 3; mems_trx_cmd((ch<> MEMS_MEAS_Pos) << 2; /* Now do an arithmetic division to sign extend */ return data / 4; } void mems_spi_init(void) { SPI1->CR1 = (1<CR2 = (15<CR1 |= SPI_CR1_SPE; } void mems_init(void) { mems_spi_init(); for (size_t i=0; i<10000; i++) { asm volatile("nop"); } /* Take accelerometer out of initialization phase */ mems_write_reg(MEMS_REG_CTRL0, 0x01); } #define WIN_LEN 8 int16_t meas_buf[WIN_LEN * 3] = {0}; size_t meas_buf_wptr = 0; size_t meas_buf_rptr = 0; int res_seq = 0; void TIM1_BRK_TIM15_IRQHandler (void) { TIM15->SR = 0; int16_t data = mems_read_meas(0); /* write into meas_buf as circular buffer */ meas_buf[meas_buf_wptr] = data; meas_buf_wptr += 1; if (meas_buf_wptr >= COUNT_OF(meas_buf)) { meas_buf_wptr = 0; } /* set read pointer to oldest 8-measurement block by rounding down meas_buf_wptr by 8, then adding 8 and wrapping */ size_t tmp = 8 * (meas_buf_wptr / 8 + 1); if (tmp >= COUNT_OF(meas_buf)) { tmp = 0; } /* Update sequence pointer when the transmission window changes. */ if (tmp != meas_buf_rptr) { res_seq += 1; meas_buf_rptr = tmp; } } int main(void) { RCC->AHBENR |= RCC_AHBENR_GPIOAEN; RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM15EN; #define AFRL(pin, val) ((val) << ((pin)*4)) #define AFRH(pin, val) ((val) << (((pin)-8)*4)) #define AF(pin) (2<<(2*(pin))) #define OUT(pin) (1<<(2*(pin))) #define IN(pin) (0) #define ANALOG(pin) (3<<(2*(pin))) #define CLEAR(pin) (3<<(2*(pin))) /* GPIO pin config: * A9: USART 1 TX -> LED * A10: USART 1 RX -> debug * A15: Accelerometer CS * A5/6/7: SPI SCK/MISO/MOSI for Accelerometer */ GPIOA->MODER &= ~(CLEAR(15)); /* Clear JTAG TDI pin mode */ GPIOA->MODER |= AF(9) | AF(10) | OUT(15) | AF(5) | AF(6) | AF(7); GPIOA->AFR[0] = AFRL(5, 5) | AFRL(6, 5) | AFRL(7, 5); GPIOA->AFR[1] = AFRH(9, 7) | AFRH(10, 7); GPIOA->BSRR = 1<<15; /* De-assert accelerometer !CS */ SystemCoreClockUpdate(); int apb2_clock = SystemCoreClock / APB2_PRESC; TIM15->PSC = apb2_clock / 1000000 * 100 - 1; /* 100us ticks */ TIM15->ARR = 1000 - 1; /* 100ms overflow interrupt interval */ TIM15->DIER = TIM_DIER_UIE; TIM15->CR1 = TIM_CR1_CEN; NVIC_EnableIRQ(TIM1_BRK_TIM15_IRQn); int baudrate = 115200; USART1->CR1 = USART_CR1_TE | USART_CR1_RE; USART1->BRR = (apb2_clock + baudrate/2) / baudrate; USART1->CR2 |= USART_CR2_RXINV; //| USART_CR2_TXINV; USART1->CR1 |= USART_CR1_UE; mems_init(); struct tx_state tx_st = { 0 }; struct res_pkt res_buf = { 0 }; uint8_t tx_buf[512]; /* int req_seq = 0; struct req_pkt req_buf = { 0 }; uint8_t rx_buf[512]; size_t rx_char = 0; unsigned int rx_overrun = 0; unsigned int rx_cobs_error = 0; unsigned int rx_framing_error = 0; unsigned int rx_crc_error = 0; */ USART1->TDR = 0; /* Kick off transmission */ while (23) { if (tx_st.remaining_bytes == 0) { res_buf.res_seq = res_seq; memcpy(res_buf.meas_data, meas_buf + meas_buf_rptr, 8 * sizeof(meas_buf[0])); memcpy(res_buf.meas_data + 8, meas_buf + ((meas_buf_rptr + 8) % COUNT_OF(meas_buf)) , 8 * sizeof(meas_buf[0])); packetize(&res_buf, &res_buf.trailer); tx_st.tx_char = tx_buf; tx_st.remaining_bytes = cobs_encode((uint8_t *)&res_buf, sizeof(res_buf), tx_buf, sizeof(tx_buf));; } if (USART1->ISR & USART_ISR_TXE && tx_st.remaining_bytes > 0) { USART1->TDR = *(tx_st.tx_char); tx_st.tx_char += 1; tx_st.remaining_bytes -= 1; } if (USART1->ISR & USART_ISR_ORE) USART1->ICR = USART_ICR_ORECF; if (USART1->ISR & USART_ISR_NE) USART1->ICR = USART_ICR_NCF; if (USART1->ISR & USART_ISR_FE) USART1->ICR = USART_ICR_FECF; if (USART1->ISR & USART_ISR_RXNE) { uint8_t c = USART1->RDR; (void) c; /* if (!c) { if (rx_char < sizeof(rx_buf)) { int rc = cobs_decode(rx_buf, rx_char, (uint8_t *)&req_buf, sizeof(req_buf)); if (rc < 0) { rx_cobs_error += 1; } else { if (rc == sizeof(req_buf)) { crc32_t check_crc = pkt_crc(&req_buf, &req_buf.trailer); if (check_crc != req_buf.trailer.crc32 || check_crc == 0 || (int)check_crc == -1) { rx_crc_error += 1; } else { req_seq = req_buf.req_seq; } } else { rx_framing_error += 1; } } } rx_char = 0; } else { if (rx_char < sizeof(rx_buf)) { rx_buf[rx_char] = c; rx_char += 1; } else { rx_overrun += 1; } } */ } } } void *memcpy(void *restrict dest, const void *restrict src, size_t n) { unsigned char *d = dest; const unsigned char *s = src; for (; n; n--) *d++ = *s++; return dest; } void *memset(void *dest, int c, size_t n) { unsigned char *s = dest; size_t k; /* Fill head and tail with minimal branching. Each * conditional ensures that all the subsequently used * offsets are well-defined and in the dest region. */ if (!n) return dest; s[0] = c; s[n-1] = c; if (n <= 2) return dest; s[1] = c; s[2] = c; s[n-2] = c; s[n-3] = c; if (n <= 6) return dest; s[3] = c; s[n-4] = c; if (n <= 8) return dest; /* Advance pointer to align it at a 4-byte boundary, * and truncate n to a multiple of 4. The previous code * already took care of any head/tail that get cut off * by the alignment. */ k = -(uintptr_t)s & 3; s += k; n -= k; n &= -4; for (; n; n--, s++) *s = c; return dest; } void __libc_init_array (void) __attribute__((weak)); void __libc_init_array () { }