Why am I only getting 0xFF when reading from the LIS3DSH accelerometer on the STM32F407G-Disc1 MCU? - embedded

So I'm learning embedded development, and I recently learned the basics of SPI. As a project, I wanted to communicate with the LIS3DSH accelerometer on my STM32F407G-DISC1 board using only the CMSIS headers.
I pasted the entire code below, but I'll explain it first because no one wants to read all that code.
As a reference, these are the pins needed (according to the MCU's datasheet) to communicate via SPI:
PA5 - SPI1_SCK
PA7 - SPI1_MOSI
PA6 - SPI1_MISO
PE3 - CS_I2C/SPI
Here's the steps I took in my code:
Enabled the clock for GPIOA and GPIOE using the AHB1ENR register.
For GPIOA, I set the three pins as alternate function, output is push-pull, speed is low, no pull-up/pull-down, and configured the alternate function as SPI.
For GPIOE, set it as GPIO mode, push-pull, low speed, pull-up, and then set it high (as in wrote to the BSSR register).
Enabled the clock for SPI using the APB2ENR register.
Configured SPI1: first disabled it, enabled 2-line unidirectional mode, set baud rate to fPCL/16 since the APB2 peripheral clock is 84MHz and the max clock of the accelerometer is 10MHz. Then set clock phase and polarity to 1. 8-bit data frame, MSB first, enabled software slave management, and also enabled master configuration. Finally, enabled SPI1.
After all this, I transmit 0x63 to the 0x20 register of the accelerometer. This sets the output rate to 100Hz and enables both the x and y axis. I have no idea if this is actually working. I'm assuming it is because the TX buffer is empty when I check the SPI status register.
Then to test whether I can recieve, I attempt to get the data from the WHO_AM_I register of the accelerometer. But I only see garbage data when I debug it (0xFF).
I've googled around to see why this may be, and a lot of people suggested that the clock polarity and phase may be incorrect. However, I've checked it multiple times, and I'm fairly certain I configured it properly.
I've tried setting interrupts. During the interrupt, even though RXNE (RX buffer not empty) is true, it still reads only 0xFF. I'm stumped as to why this is happening.
The code is below. The starting point is accelerometer_init(). The reading of the data from the WHO_AM_I register is in turn_on_accelerometer().
#include <stdint.h>
#include <stdbool.h>
#include "stm32f4xx.h"
#include "accelerometer.h"
static void gpio_clock_enable(void);
static void gpio_a_init(void);
static void gpio_e_init(void);
static void accelerometer_clock_enable(void);
static void configure_accelerometer(void);
static void pull_slave_high(void);
static void pull_slave_low(void);
static void turn_on_accelerometer(void);
static void wait_till_transmit_complete(void);
static void transmit_only(uint8_t address, uint8_t data);
static void receive_dummy_data(void);
void accelerometer_init(void) {
gpio_clock_enable();
gpio_a_init();
gpio_e_init();
accelerometer_clock_enable();
configure_accelerometer();
turn_on_accelerometer();
}
void gpio_clock_enable(void) {
RCC_TypeDef *rcc = RCC;
rcc->AHB1ENR |= (1 << 0) | (1 << 4);
}
void gpio_a_init(void) {
GPIO_TypeDef *gpio_a = GPIOA;
// Reset mode and set as alternate function
gpio_a->MODER &= ~(0x3 << 10) & ~(0x3 << 12) & ~(0x3 << 14);
gpio_a->MODER |= (0x2 << 10) | (0x2 << 12) | (0x2 << 14);
// Set output to PP
gpio_a->OTYPER &= ~(1 << 5) & ~(1 << 6) & ~(1 << 7);
// Set speed to low
gpio_a->OSPEEDR &= ~(0x3 << 10) & ~(0x3 << 12) & ~(0x3 << 14);
// Set to no pull-up / pull-down
gpio_a->PUPDR &= ~(0x3 << 10) & ~(0x3 << 12) & ~(0x3 << 14);
// Reset alternate function and set to SPI
gpio_a->AFR[0] &= ~(0xF << 20) & ~(0xF << 24) & ~(0xF << 28);
gpio_a->AFR[0] |= (0x5 << 20) | (0x5 << 24) | (0x5 << 28);
}
void gpio_e_init(void) {
GPIO_TypeDef *gpio_e = GPIOE;
// Set as general purpose output mode
gpio_e->MODER &= ~(0x3 << 6);
gpio_e->MODER |= (1 << 6);
// Set as push pull
gpio_e->OTYPER &= ~(1 << 3);
// Set as low speed
gpio_e->OSPEEDR &= ~(0x3 << 6);
// Set to pull up
gpio_e->PUPDR &= ~(0x3 << 6);
gpio_e->PUPDR |= (1 << 6);
// Set it high
pull_slave_high();
}
void accelerometer_clock_enable(void) {
RCC_TypeDef *rcc = RCC;
rcc->APB2ENR |= (1 << 12);
}
void configure_accelerometer(void) {
SPI_TypeDef *spi_1 = SPI1;
// First disable it while we configure SPI
spi_1->CR1 &= ~(1 << 6);
// 2-line unidirectional data mode enabled
spi_1->CR1 &= ~(1 << 15);
// Reset baud rate and set to fPCLK/16
// because APB2 peripheral clock currently is 84 MHz
// and the max clock of the accelerometer is 10 MHz.
spi_1->CR1 &= ~(0x7 << 3);
spi_1->CR1 |= (0x3 << 3);
// Set clock phase to 1
spi_1->CR1 |= (1 << 0);
// Set clock polarity to 1
spi_1->CR1 |= (1 << 1);
// 8 bit data frame format
spi_1->CR1 &= ~(1 << 11);
// MSB first
spi_1->CR1 &= ~(1 << 7);
// Software slave management enabled
spi_1->CR1 |= (1 << 9);
spi_1->CR1 |= (1 << 8);
// Master configuration enabled
spi_1->CR1 |= (1 << 2);
// SS output enabled
// spi_1->CR2 |= (1 << 2);
// Enable SPI
spi_1->CR1 |= (1 << 6);
// Wait a little bit for accelerometer to turn on
for (int i=0; i<1000000; i++);
}
void pull_slave_high(void) {
// Wait until SPI is no longer busy
SPI_TypeDef *spi_1 = SPI1;
while ((spi_1->SR >> 7) & 1);
GPIO_TypeDef *gpio_e = GPIOE;
gpio_e->BSRR |= (1 << 19);
}
void pull_slave_low(void) {
// Wait until SPI is no longer busy
SPI_TypeDef *spi_1 = SPI1;
while ((spi_1->SR >> 7) & 1);
GPIO_TypeDef *gpio_e = GPIOE;
gpio_e->BSRR |= (1 << 3);
}
void turn_on_accelerometer(void) {
// Set output data rate to 100Hz
// and enable X-axis, Y-axis.
transmit_only(0x20, 0x63);
receive_dummy_data();
// Temp test checking the WHO_AM_I register on the accelerometer.
SPI_TypeDef *spi_1 = SPI1;
pull_slave_low();
wait_till_transmit_complete();
uint8_t address = 0x0F | 0x80;
spi_1->DR = address;
wait_till_transmit_complete();
while (true) {
volatile bool is_busy = (spi_1->SR >> 7) & 1;
volatile bool is_rx_buffer_not_empty = (spi_1->SR >> 0) & 1;
if (!is_busy && is_rx_buffer_not_empty) {
break;
}
}
volatile uint32_t data = spi_1->DR;
pull_slave_high();
}
/*
* Transmit is synchronous.
*/
void transmit_only(uint8_t address, uint8_t data) {
SPI_TypeDef *spi_1 = SPI1;
// Select the accelerometer as the slave
pull_slave_low();
// Wait till transmit buffer is ready
wait_till_transmit_complete();
spi_1->DR = address;
// Wait till transmit buffer is ready
wait_till_transmit_complete();
spi_1->DR = data;
// Wait till transmit buffer has been read
wait_till_transmit_complete();
// Deselect the slave
pull_slave_high();
}
void wait_till_transmit_complete(void) {
SPI_TypeDef *spi_1 = SPI1;
while (true) {
volatile bool is_busy = (spi_1->SR >> 7) & 1;
volatile bool is_transmit_buffer_empty = (spi_1->SR >> 1) & 1;
if (!is_busy && is_transmit_buffer_empty) {
break;
}
}
}
void receive_dummy_data(void) {
SPI_TypeDef *spi_1 = SPI1;
spi_1->DR;
spi_1->SR;
}

You are working with SPI incorrectly.
This bus works in this way:
master (MCU) sends byte in MOSI
line at the same (!) time slave (LIS) sends byte in MISO line. In this moment slave dont know, what exactly byte master transfers to it.
To transfer one byte, you should:
write byte in data register
wait for completition of transfer
read data register
Thus, to read WHO_AM_I register, we obtain next sequence:
init SPI
flush data register (just read SPI->DR)
send command
wait
read dummy data (your 0xFF)
write second byte (0x00 or 0xFF, it doesn't matter)
wait
read correct answer from LIS

Related

STM32F7, Bare Metal C, QSPI Indirect Mode Hangs or Writes Same Gibberish

Win 10, STM32CubeIDE 1.8.0
STM32F746 Discovery board
MCU: STM32F746 (Datasheet, Reference Manual, Errata)
Flash: MT25QL128ABA1EW9-0SIT (Datasheet)
Equipment: Low end oscilloscope, low end logic analyzer with decoder
What I'm trying to achieve: I want to be able to send command via indirect write (works OK), read register with indirect read (fails miserably with consistent garbage on the lines), haven't even tried to read/write actual memory.
Connections (from the discovery board schematic):
Interestingly enough, the example provided by STMicoelectronics themselves also doesn't work as expected. More on that later.
Initially, I read the reference manual and tried to figure the stuff out on my own, as I always do when I learn to operate new peripheral. It didn't exactly work out, so I used TouchGFX-generated code to compare configuration (it's using memory mapped mode, but I could at least check clock and GPIO setup, and it was correct), then I also found pretty much the only other article that does what I do. I was close, but a few unexplained (not covered in reference manual) bits from the article made it work. (Article)
I used only the early code from there. Up to and including the first bit of code under "Initialization" section, but not anything further. I adjusted it for my Flash size (128Mbit).
I will safely assume my clock and GPIO initialization is correct, as it matches TouchGFX code, which utilizes QSPI Flash, as well as example program from STM32F7 package.
I'm configuring QSPI with 1MHz clock. While it's not exactly covered in specs of the Flash IC, it's easier on my scope and logic analyzer, both of which have 100MHz as documented upper bound, but they're not really usable there. I also tried to use 108MHz, which is a documented Flash IC spec, I still get garbage there (found via debugging output).
QSPI setup:
void qspi_setup_indirect_mode(void) {
/* ------------ QSPI Initialization --------------- */
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Flash size 128Mb=16MB=2^24 bytes
* 24-bit Address
*
* */
QUADSPI->DCR = 0x00; //reset
QUADSPI->CCR = 0x00; //reset
QUADSPI->DCR |= (23U << QUADSPI_DCR_FSIZE_Pos);
QUADSPI->CCR |= (2U << QUADSPI_CCR_ADSIZE_Pos);
/*
* Sample shift 1/2 clock cycle
* Prescaler = 2 (216MHz/216 = 1MHz)
*
* */
QUADSPI->CR = 0x00; //reset
QUADSPI->CR |= (QUADSPI_CR_SSHIFT | (215U << QUADSPI_CR_PRESCALER_Pos));
/*
* Make sure all flags are cleared
*
* */
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF;
/*
* Enable peripheral
* */
//QUADSPI->CR |= (QUADSPI_CR_EN); (enable later for every transmission)
}
Then there is function, that sets command mode. It sets access mode (indirect write, read, polling, memory mapped), as well as on how many datalines instruction, address and so on are transmitted (from none to 4), and dummy cycles. Nothing fancy, very similar to the one from the example.
void qspi_set_command_mode(uint8_t fmode, uint8_t imode, uint8_t admode, uint8_t abmode, uint8_t dcyc, uint8_t dmode) {
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Communication configuration register
* First, reset all mode values
* Set new values
* */
QUADSPI->CCR = QUADSPI->CCR & ~(QUADSPI_CCR_FMODE) & ~(QUADSPI_CCR_IMODE) & ~(QUADSPI_CCR_ADMODE) & ~(QUADSPI_CCR_ABMODE) & ~(QUADSPI_CCR_DCYC)
& ~(QUADSPI_CCR_DMODE);
QUADSPI->CCR = QUADSPI->CCR | (fmode << QUADSPI_CCR_FMODE_Pos) | (imode << QUADSPI_CCR_IMODE_Pos) | (admode << QUADSPI_CCR_ADMODE_Pos)
| (abmode << QUADSPI_CCR_ABMODE_Pos) | (dcyc << QUADSPI_CCR_DCYC_Pos) | (dmode << QUADSPI_CCR_DMODE_Pos);
}
I tried various minor changes to these functions, and write works if and only if I disable peripheral, configure the thing, enable, set the instruction. If I enable peripheral in the setup section, write doesn't work. This is not covered in reference manual, I found it in the article (where it's not pointed out).
void qspi_sendCommandIndirectWrite(uint8_t command) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x00, 0x01, 0x00, 0x00, 0x00, 0x00); //Set indirect write, only instruction on 1 line, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
void qspi_sendCommandIndirectRead(uint8_t command, uint8_t receiveBuffer[], uint32_t length) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x01, 0x01, 0x00, 0x00, 0x01, 0x01); //Set indirect write, only instruction on 1 line, , data on 1 line, 1 dummy cycle, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->DLR = length;
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
uint32_t counter = 0x00;
while (counter < length) {
while (!(QUADSPI->SR & QUADSPI_SR_TCF)); //wait while data arrives to FIFO
receiveBuffer[counter] = (uint8_t) (0xFF & QUADSPI->DR);
counter++;
}
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
Finally, all of that is called in the main the following way:
#include "main.h"
void system_hw_setup(void);
void qspi_example(void);
int main(void) {
system_hw_setup(); //initialize hardware
system_msdelay(100U);
//qspi_sendCommandIndirectWrite(MT25QL128ABA1EW9_COMMAND_ENTER_QUAD_IO_MODE); //works OK
//qspi_example(); //example provided by STM32 w clock and GPIO setup
system_msdelay(100U);
uint8_t test[1];
while (1) {
qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_STATUS_REGISTER, test, 1);
//qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_ENHANCED_VOLATILE_CONFIGURATION_REGISTER, test, 1);
system_msdelay(100U);
toggle_stm32f746disco_ld1();
/*
test[0] = 0x00;
if (test[0] == 0x00) {
test[0] = (uint8_t) '0';
}
/usart_dma_sendArray(USART1, test, 1); */
}
}
void system_hw_setup(void) {
rcc_setup(); //clock for peripheral, clock will not be altered; therefore default HSI 16MHz
systick_setup(SYSTEM_FREQUENCY); //activate systick
gpio_setup(); //set pin modes and functions
dma_reset_flags(DMA2); //clear DMA2 flags for USART1
dma_reset_flags(DMA1); //clear DMA1 flags for I2C3
usart_dma_setup(USART1); //set control registers and settings for USART1 and its DMA connected to st-link
usart_enable(USART1); //enable uart1
usart_enable_tx(USART1); //enable tx line (wrapper)
usart_enable_rx(USART1); //enable rx line (wrapper)
qspi_setup_indirect_mode(); //enable qspi in indirect mode
nvic_setup(); //set interrupts and their priorities
}
which gives the following:
As per reference manual, data from the IC should come on DQ1, but it's not happening. Also, sometimes DQ3 randomly goes up for some time. The number of clock cycles is strange. Also, I have no idea why there is some 0x80 packet there, I'm sending only the instruction and nothing else. It could be related to my artificially lowered clock speed, but the same configuration also miserably fails if I set QSPI clock to proper value.
I'm pretty lost at what I'm doing wrong, and the reference manual section of the MCU is not much help at this point, and there are next to no resources on the internet that cover it in a meaningful (or any, at this point) way.
I would appreciate any help or advice with making QSPI work!
The main problem was the access to data register. QUADSPI->DR is a volatile uint32_t. So whenever I access QUADSPI->DR, even if I received 1 byte, it reads 4 bytes from the register, and it also produces gibberish with FIFO threshold because of that. The correct solution is to explicitly specify byte, half-word or word access to the QUADSPI->DR. I take address of the data register, cast it as a pointer to uint8_t or uint16_t, and dereference it:
uint32_t mydata = QUADSPI->DR;
uint16_t mydata = *(uint16_t*)(&QUADSPI->DR);
uint8_t mydata = *(uint8_t*)(&QUADSPI->DR);

Reading a non-null terminated input at serial?

I am working with mbed, the LPC1768. The serial input to the mbed is a unsigned character array which is not null-terminated. I don't get anything with getc(), how to get the input?
the null character is 0 so if you need get it u can using a Buffer Counter or a ovf variable to detect received a character!!
Config your UART0,1 by this Setting:
comment UART0 or UART1 if not use it!
so u can select UARTs with #define in: option->C/C++->Define:__UART0 or __UART1
void SER_Init (void) {
#ifdef __UART0 /* UART0 */
LPC_SC->PCONP |= ((1 << 3) | (1 << 15)); /* enable power to UART0 & IOCON */
LPC_PINCON->PINSEL0 |= (1 << 4); /* Pin P0.2 used as TXD0 */
LPC_PINCON->PINSEL0 |= (1 << 6); /* Pin P0.3 used as RXD0 */
#else /* UART1 */
LPC_SC->PCONP |= ((1 << 4) | (1 << 15)); /* enable power to UART1 & IOCON */
LPC_PINCON->PINSEL4 |= (2 << 0); /* Pin P2.0 used as TXD1 */
LPC_PINCON->PINSEL4 |= (2 << 2); /* Pin P2.1 used as RXD1 */
#endif
UART->LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */
UART->DLL = 9; /* 115200 Baud Rate # 25.0 MHZ PCLK*/
UART->FDR = 0x21; /* FR 1,507, DIVADDVAL=1, MULVAL=2 */
UART->DLM = 0; /* High divisor latch = 0 */
UART->LCR = 0x03; /* DLAB = 0 */
}
------> and use this func. for Get Character:
/*----------------------------------------------------------------------------
Read character from Serial Port (blocking read)
*----------------------------------------------------------------------------*/
int SER_GetChar (void) {
while (!(UART0->LSR & 0x01));
return (UART0->RBR);
}

STM32F3 Dual ADC with interleaved mode

I'm trying to achieve 10MSPS as documented in STM32F30x ADC modes and application under the section Dual interleaved mode.
Firstly, i tried to use single DMA. I configured the DMA1 Channel1 to read from ADC1&2 Common data register. It worked but i could only achieve a sample rate of 8.47MSPS. Beyond that limit, ADC1 starts to overrun.
(Register ADC1_2->CCR: MULT=0x07, MDMA=0x02, DELAY=0x04) Considering the DMA reading the common data register after the slave adc ends its conversion, the problem seems reasonable at high sample rates.
So i decided to use 2 DMAs. One for each ADC:
DMA1 Channel1 copies from ADC1->DR to SRAM
DMA2 Channel1 copies from ADC2->DR to SRAM
(Register ADC1_2->CCR: MULT=0x07, MDMA=0x00, DELAY=0x04)
This configuration also worked but again up to 8MSPS. Above that rate, ADC2 starts to overrun. I cannot understand why ADC2 overruns. I expected that this setup would work.
When i run ADC1 & ADC2 in independent mode with DMA configuration above, everything seems to work fine. No overruns, both ADC samples at 5.1MSPS but independently.
One question: What happens when both ADCs run in independent mode and triggered from the same source (e.g. TIM2) but ADC1 is triggered at the rising edge and ADC2 is triggered at the falling edge of the clock ? Would it work? This is the next thing i will try.
The MCU i work with is STM32F303CB.
ADC sampling times were 1.5 Cycles.
Any advice will be appreciated.
Edit: I have provided a minimal sample code that runs on STM32F3 Discovery with an 8 MHz Crystal. Program directly jumps to main()
// main.c
#include "stm32f30x.h"
#define DUALDMA
void sysinit();
void clockconfig();
void delay(int d);
void timerinit();
void adcinit();
void dmainit();
void dualdmainit();
int main(){
sysinit();
clockconfig();
timerinit();
#ifdef DUALDMA
dualdmainit();
#else
dmainit();
#endif
adcinit();
RCC->AHBENR |= RCC_AHBENR_GPIOEEN; // GPIOE enable
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // GPIOA enable
GPIOE->MODER = 0x55555555; // GPIOE -> output
GPIOA->MODER |= 0x0000FFFF;// GPIOA -> analog
// Reset SRAM memory area
for(int i = 0;i<1024*4;i+=4){
*((uint32_t*)(0x20000800+i)) = 0;
}
// Blink LEDs
while(1){
GPIOE->ODR = 0xFFFF;
delay(1000);
GPIOE->ODR = 0x00FF;
delay(1000);
}
}
void delay(int d){
// Dummy delay
int l = d*1000;
for(int i = 0;i<l;i++);
}
void sysinit(){
//STM32F303 reset state
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= 0x00000001U;
/* Reset CFGR register */
RCC->CFGR &= 0xF87FC00CU;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= 0xFEF6FFFFU;
/* Reset HSEBYP bit */
RCC->CR &= 0xFFFBFFFFU;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE bits */
RCC->CFGR &= 0xFF80FFFFU;
/* Reset PREDIV1[3:0] bits */
RCC->CFGR2 &= 0xFFFFFFF0U;
/* Reset USARTSW[1:0], I2CSW and TIMs bits */
RCC->CFGR3 &= 0xFF00FCCCU;
/* Disable all interrupts */
RCC->CIR = 0x00000000U;
SCB->VTOR = 0x08000000; /* Vector Table Relocation in Internal FLASH */
}
void adcinit(){
RCC->AHBENR |= RCC_AHBENR_ADC12EN; // Enable ADC clock
RCC->CFGR2 |= RCC_CFGR2_ADCPRE12_4;// ADC clock prescaler = 1
ADC1->CFGR |= ADC_CFGR_EXTEN_0; // Trigger on rising edge
ADC1->CFGR |= ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1; // TIM1 TRGO2
ADC1->SQR1 |= ADC_SQR1_SQ1_0 ; // ch 1
ADC1->CFGR |= ADC_CFGR_OVRMOD; // Stop on overrun
ADC1->CFGR |= ADC_CFGR_DMAEN; // DMA enable
ADC1->CR &= ~(ADC_CR_ADVREGEN_1 | ADC_CR_ADVREGEN_0); // Enable VREG
ADC1->CR |= ADC_CR_ADVREGEN_0;
ADC1->CR |= ADC_CR_ADEN;
while( (ADC1->ISR & ADC_ISR_ADRD) == 0 );
ADC2->SQR1 |= ADC_SQR1_SQ1_0 ; // ch 1
ADC2->CFGR |= ADC_CFGR_DMAEN;
ADC2->CR &= ~(ADC_CR_ADVREGEN_1 | ADC_CR_ADVREGEN_0);
ADC2->CR |= ADC_CR_ADVREGEN_0;
ADC2->CR |= ADC_CR_ADEN;
while( (ADC1->ISR & ADC_ISR_ADRD) == 0 );
ADC1_2->CCR |= ADC12_CCR_DELAY_2 ; // Delay = 4, 5 Cycles
#ifndef DUALDMA
ADC1_2->CCR |= ADC12_CCR_MDMA_1; // If single DMA is selected, configure MDMA bits for 12 bits
#endif
ADC1_2->CCR |= ADC12_CCR_MULTI_2 | ADC12_CCR_MULTI_1 | ADC12_CCR_MULTI_0; // Interleaved mode
}
void dmainit(){
// DMA config for Single DMA, 32 bits
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
DMA1_Channel1->CPAR = (uint32_t)&ADC1_2->CDR;
DMA1_Channel1->CMAR = 0x20000800;
DMA1_Channel1->CNDTR = 1024;
DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_1 | DMA_CCR_PSIZE_1;
//DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC ;
}
void dualdmainit(){
// DMA config for DUAL DMA, 16bits
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // DMA1 Enable
RCC->AHBENR |= RCC_AHBENR_DMA2EN; // DMA2 Enable
DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
DMA1_Channel1->CMAR = 0x20000800;
DMA1_Channel1->CNDTR = 1024;
DMA1_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;
DMA2_Channel1->CPAR = (uint32_t)&ADC2->DR;
DMA2_Channel1->CMAR = 0x20000800+1024*2;
DMA2_Channel1->CNDTR = 1024;
DMA2_Channel1->CCR = DMA_CCR_EN | DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0;
}
void timerinit(){
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Enable TIM1
TIM1->CR2 |= TIM_CR2_MMS2_1; // Update event selected as TRGO2
TIM1->PSC = 0;
TIM1->ARR = 0x0d; // 5 MHz (72 MHz / 14 )
TIM1->CR1 |= TIM_CR1_CEN;
}
void clockconfig(){
// External oscillator (HSE): 8MHz
RCC->CR |= RCC_CR_HSEON; // Enable HSE
while( (RCC->CR & RCC_CR_HSERDY) == 0 );
RCC->CFGR |= RCC_CFGR_PLLMULL9; // PLL MUL = x9
RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; // APB1 Prescaler = 2
RCC->CFGR |= RCC_CFGR_PLLSRC; // PLL source = HSE
FLASH->ACR |= FLASH_ACR_LATENCY_1; // Two wait states
RCC->CR |= RCC_CR_PLLON; // Enable and wait PLL
while( (RCC->CR & RCC_CR_PLLRDY) == 0 );
RCC->CFGR |= RCC_CFGR_SW_PLL; // Select PLL as system clock
}
Scatter file:
LR_IROM1 0x08000000 0x00020000 { ; load region size_region
ER_IROM1 0x08000000 0x00020000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM2 0x10000000 0x00000200 { ; RW data
.ANY (+RW +ZI)
}
}
You cant do it this way. You need to use only one DMA channel and both samples are transmitted in one 32 bit DMA transaction.
In 6 bits mode I have archived more than 18MSPS
I do not know how to program it using HAL as I personally do only the bare register approach
There is a hardware problem as well (read the errata) and sometimes in >8bit modes the transfer does not work properly.
For dual DMA you need to:
Prevent any core accesses to the SRAM memory by placing the stack and the variables (except the ADC buffers) in the CCM RAM or suspending any core activity by entering the sleep mode.

Attiny 84 Communicating with RTC Through SPI Troubles

I am currently trying to use an ATtiny84 to communicate with an RTC (DS1305) through SPI to make an buzzer vibrate every variable amount of time. I've been trying to set alarm0 on the DS1305. However, the 84 does not "technically" have SPI. It has USI which can be programmed to be like SPI. I was wondering if any of you could review my code/ board connections and let me know if you see any problems. The current problem is that I cannot get any communication going through SPI and I am having trouble finding what the issue could be.
Current board connections:
ATtiny84 | DS1305
MOSI ------ DI
MISO ------ DO
USCLK ---- CLK
Datasheets:
Attiny84
DS1305
/*
* Atmel_Jolt_Code.c
*
* Created: 11/28/2018 10:44:30 PM
* Author : Nick Hulsey
*/
#include <avr/io.h>
#define F_CPU 16000000UL
#include <avr/interrupt.h>
#include <util/delay.h>
//variables for SPI
#define SPI_DDR_PORT DDRA
#define CE_PIN DDA3 //I ADDED *****
#define DO_DD_PIN DDA5 // SHOULD WE
#define DI_DD_PIN DDA6 // THEM FLIP
#define USCK_DD_PIN DDA4
#define SPI_MODE0 0x00
#define SPI_MODE1 0x04
#define MOTOR_PIN DDA7 //I ADDED *****
void SPI_begin();
void setDataMode(uint8_t spiDataMode);
uint8_t transfer(uint8_t spiData);
void flipLatch(uint8_t on);
int main(void)
{
SPI_begin();
setDataMode(SPI_MODE1);
DDRA |= (1 << MOTOR_PIN);
//**startup**
uint8_t status_register = 0x10;
uint8_t control_register = 0x8F;
uint8_t control_byte = 0x05;
uint8_t alarm_registers[] = {0x8A, 0x89, 0x88, 0x87};
//set control
flipLatch(1);
transfer(control_register);
transfer(0);
flipLatch(0);
flipLatch(1);
transfer(control_register);
transfer(control_byte);
flipLatch(0);
//set alarm:
for (int i = 0; i < 4; i++){
flipLatch(1);
transfer(alarm_registers[i]);
transfer(0x80); //0b10000000
flipLatch(0);
}
//THIS MIGHT NEED WORK
//GIMSK |= (1 << PCIE1);//set external interrupt (A1)
PCMSK0 |= (1 << PCINT1);
sei();
while (1) //our main loop
{
//reading the flag from the status register
uint8_t status = transfer(status_register);
if(status == 0x01){//if alarm 0 has been flagged
PORTA ^= (1 << MOTOR_PIN);
_delay_ms(100);
}
}
}
//if A1 has changed state at all this function will fire
ISR(PCINT1_vect){
PORTA ^= (1 << MOTOR_PIN);//invert motor power
_delay_ms(100);
}
void SPI_begin(){
USICR &= ~((1 << USISIE) | (1 << USIOIE) | (1 << USIWM0));//Turn off these bits
USICR |= (1 << USIWM0) | (1 << USICS1) | (1 << USICLK);//Turn on these bits
//REVIEW THIS PAGE 128
//external,positive edge software clock
//What does this mean
SPI_DDR_PORT |= 1 << USCK_DD_PIN; // set the USCK pin as output
SPI_DDR_PORT |= 1 << DO_DD_PIN; // set the DO pin as output
SPI_DDR_PORT |= 1 << CE_PIN;// ******** I ADDED
SPI_DDR_PORT &= ~(1 << DI_DD_PIN); // set the DI pin as input
}
void setDataMode(uint8_t spiDataMode)
{
if (spiDataMode == SPI_MODE1)
USICR |= (1 << USICS0);
else
USICR &= (1 << USICS0);
}
//returns values returned from the IC
uint8_t transfer(uint8_t spiData)
{
USIDR = spiData;
USISR = (1 << USIOIF); // clear counter and counter overflow interrupt flag
//ATOMIC_BLOCK(ATOMIC_RESTORESTATE) // ensure a consistent clock period
//{
while ( !(USISR & (1 << USIOIF)) ) USICR |= (1 << USITC);
//}
return USIDR;
}
void flipLatch(uint8_t on){
if (on == 1)
PORTA |= (1 << CE_PIN);
else
PORTA &= ~(1 << CE_PIN);
}

atmega 328p interrupt and timer setup [C/C++]

I am trying to set up one interrupt and one counter/timer. The interrupt is external, reading low logic from pin. Timer should increment every 100 us and add up to count variable.
I've set up the interrupt, which is working fine however, after setting up a timer, neither interrupt nor timer works. The code is such:
volatile boolean allowCount = false, timerFlag = false;
volatile unsigned int counter;
boolean pulseLow = false;
void setup(){
Serial.begin(9600);
// initialize external pin interrupt.
PCICR = 0b00000010; // 1. PCIE1: Pin Change Interrupt Enable 1
EICRA |= bit (ISC10); // set wanted flags (low logic level causes interrupt)
PCMSK1 = 0b00010000; // Enable Pin Change Interrupt for A4
// TODO Interrupt settings not working together
// initialize Timer1
cli(); // disable global interrupts
TCCR1A = 0; // set entire TCCR1A register to 0
TCCR1B = 0; // same for TCCR1B
// set compare match register to desired timer count:
OCR1A = 0x18;
// turn on CTC mode:
TCCR1B |= (1 << WGM12);
// Set CS10 and CS12 bits for 64 prescaler:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS11);
// enable timer compare interrupt:
TIMSK1 |= (1 << OCIE1A);
}
void loop(){
if (allowCount == true)
{ timer100_uS();
if (counter > 50 && pulseLow == false){
DDRC |= (1 << DDC3 ); // sets bit DDC3 to 1 within register DDRC
//set pin 3(A3) ouput as sourcing Vcc(HIGH)
PORTC |= (1 << PORTC3);
timerReset();
pulseLow = true;
}
if (pulseLow == true){
timer100_uS();
if (counter >= 500){
//set pin3 of port C to LOW (A3);
PORTC &= ~(1 << PORTC3);
pulseLow = false
timerReset();
}
}
// external pin interrupt
ISR(PCINT1_vect){
if (allowCount == false)
allowCount = true;
}
// timer/counter interrupt
ISR (TIMER1_COMPA_vect)
{
if (timerFlag == true){
counter++;
}
}
void timer_100uS(void){
timerFlag = true;
cli();
}
void timerReset(void){
sei();
timerFlag = false;
counter = 0;
}
Value of OCR0A is calculated to be 24 (0x18) with prescaler 64 and 16 MHz processor based on this formula:
OCRn = [ (clock_speed / Prescaler_value) * Desired_time_in_Seconds ] - 1
How to set up different interrupts so that they don't overlap eachother ?
Or better yet, is it possible to set up timer so that it does not use interrupt ?
Thanks for reading !
As I can see, you are using ATMega328 with Arduino libraries. Your problem is that Arduino library internally uses Timer 1 for its internal purposes. Therefore if you add your own interrupt handler for Timer 1, you override Arduino's interrupt handler for Timer 1 which breaks the library.
If you want to stay with Arduino library, use the library also to control the timer: Arduino Timer1
Thank you for answers #old_timer, #klasyc.
Quite late, but I solved it by using timer0 instead of timer1 with following settings in setup:
// initialize external pin interrupt.
PCICR = 0b00000010; // 1. PCIE1: Pin Change Interrupt Enable 1
EICRA |= bit (ISC10); // set wanted flags (falling edge causes interrupt)
PCMSK1 = 0b00001000; // Enable Pin Change Interrupt for A3
TCCR0B = 0;
TCCR0A = 0;
//set CTC mode
TCCR0A = ( 1 << WGM01 );
// set OCR0A value for 1 us counter (OCRxn = (freq/prescaler)*desired_value)-1
OCR0A = 15;
// set compare match counter
TIMSK0 |= (1 << OCIE0A);
//set prescaler
TCCR0B |= ( 1 << CS00);
and outside the loop:
ISR(TIMER0_COMPA_vect){
counter++;
}