I'm trying to exchange data between 2 stm32f407 boards through uart. The peripheral that I'm using is the USART2 (there are more of them on the board). I've configured it with a baud rate of 9600, 8 bit frame, no parity bits and only 1 stop bit. The strange thing that happens is that the receiver gets a lot of null bytes (to be more precise sometimes there are 9 consecutive null bytes, while in other cases 8 null bytes ended by a 0xFF byte) before the data that I'm actually sending. This seems to happen only when I turn on the sender or when I press its reset button, if I send the same data multiple times in a row everything is fine.
This is the code that I use to setup the uart as described:
int uart_init ()
{
unsigned int ra;
ra=GET32(RCC_AHB1ENR);
ra|=1<<0; //enable port A
PUT32(RCC_AHB1ENR,ra);
ra=GET32(RCC_APB1ENR);
ra|=1<<17; //enable USART2
PUT32(RCC_APB1ENR,ra);
//PA2 USART2_TX
//PA3 USART2_RX
ra=GET32(GPIOA_MODER);
ra|= (2<<4);
ra|= (2<<6);
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=(1<<2);
ra&=(1<<3);
PUT32(GPIOA_OTYPER,ra);
ra=GET32(GPIOA_AFRL);
ra|=(7<<8);
ra|=(7<<12);
PUT32(GPIOA_AFRL,ra);
// divisor 136 fractional divisor 11
PUT32(USART2_BRR,(136<<4)|(11<<0));
PUT32(USART2_CR1,(1<<13)|(1<<3)|(1<<2));
return(0);
}
While this is my routine to send a byte through uart:
void uart_putc ( unsigned int x )
{
while (( GET32(USART2_SR) & (1<<7)) == 0) continue;
PUT32(USART2_DR,x);
}
My question is: is this a normal and reasonable behavior? In case it is, what is a good strategy to receive the stream of incoming bytes discarding those that are undesired? In case it is not, what am I doing wrong?
It's because during the interval between reset and the pins being configured for AFIO they're in a high-impedance state so attempts to read will return undefined data. Your receiver is becoming ready before your transmitter.
To guard against this you need to indicate readiness through an out of band method or if your resets are electrically linked then a dumb method such as sleeping for a second on the receiver and 2 seconds on the transmitter upon startup and after the pins have been configured for AFIO should do it.
Related
Please help! I am using FSMC to connect a STM32F407 MCU with AD7606 to sample voltage value. MCU would send sampled values to PC using USB HS port after 1024 conversions. But when I inspect the values from PC, I found that readings from channel 0 occasionally contains data from other channels. For example, if connect channel 0 to 5v, connect channel 8 to 3.3v, connect other channels to ground. Then the printed value from channel 0 would contain 5v, 0v, 3.3v. The basic setup is as follows:
A 200KHZ PWM single is generated by TIM10 to act as CONVST signal for AD7606.
7606 will then issue a BUSY signal which I used as an external interrupt source.
In the Interrupt handler, An DMA request would be issued to read 8 16bit data
from FSMC address space to memory space. TIM10 PWM would be stopped if 1024
conversions has been done.
In the DMA XFER_CPLT call back, if 1024 conversions has been done, the converted
data would be sent out by USB HS port, and TIM10 PWM would be enabled again.
Some code blocks:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_7)
{
// DMA data from FSMC to memory
HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0, 0x6C000000, (uint32_t)(adc_data + adc_data_idx) , 8);
adc_data_idx += 8;
if (adc_data_idx >= ADC_DATA_SIZE)
HAL_TIM_PWM_Stop(&htim10, TIM_CHANNEL_1);
}
}
void dma_done(DMA_HandleTypeDef *_hdma)
{
int i;
int ret;
// adc_data[adc_data_idx] would always contain data from
// channel 1, led1 wouldn't light if every thing is fine.
if (adc_data[adc_data_idx] < 0x7f00 )
HAL_GPIO_WritePin(led1_GPIO_Port, led1_Pin, GPIO_PIN_SET);
if (adc_data_idx >= ADC_DATA_SIZE)
{
if(hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED)
{
// if I don't call CDC_Transmit_HS, everything is fine.
ret = CDC_Transmit_HS((uint8_t *)(adc_data), ADC_DATA_SIZE * 2 );
if (ret != USBD_OK)
{
HAL_GPIO_WritePin(led1_GPIO_Port, led2_Pin, GPIO_PIN_SET);
}
}
adc_data_idx = 0;
HAL_TIM_PWM_Start(&htim10, TIM_CHANNEL_1);
}
}
It seems that a single USB transaction would take longer than 5us(one conversion time), so I stopped PWM signal to stop conversion...
If I only send the second half of the data buffer, there is no data mixture. It's very strange.
According to your description, I think the processing is correct, and the problem is at the CDC_Transmit_HS(); I have met the problem on the CDC_Transmit_FS(), which can't transmit more than 64 bytes data for original code, and need to modify some code, otherwise the some error occurs. Did you check the number of received data is correct?
Reference:
I can't receive more than 64 bytes on custom USB CDC class based STM32 device
I'm not sure your ADC_DATA_SIZE size; if it's larger than 64 bytes, maybe you can modify to smaller than 64 bytes and try again and check whether or not the data is correct. I am not sure if it is affected by this problem, but I think you can give it a try.
On the other hand, it may also be necessary to GND the ADC IN pins not used by AD7606 to avoid interference between channels.
Or you can try other communication (I2C, SPI, UART...etc) to send the data.
If there is no problem with other communication methods, there is a high chance that it is a problem with CDC_Transmit_HS(). If there are problems with other transmission methods, you may have to check whether there is a conflict between the ADC conversion time or the transmission time.
I believe I understand how to use interrupts to receive serial data on UART of an ATmega328p, but I don't understand the mechanics of how to transmit data.
Here is a basic program that I want to use to transmit the character string "hello" using interrupts to drive transmission. I understand that the character 'o' will likely be transmitted twice, and I am ok with that.
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
#define BAUD 19200
#define DOUBLE_SPEED 1
void initUART(unsigned int baud, unsigned int speed);
volatile uint8_t charIndex = 0;
volatile unsigned char command[5] = "hello";
int main(void)
{
//initialize UART
initUART(BAUD, DOUBLE_SPEED);
sei();
//What do I put here to initiate transmission of character string command?
//Is this even correct?
UDR0 = command[0];
while(1)
{
}
}
ISR(USART_TX_vect)
{
// Transmit complete interrupt triggered
if (charIndex >= 4)
{
//Reach the end of command, end transmission
return;
}
//transmit the first char or byte
UDR0 = command[charIndex];
//Step to the next place of the command
charIndex++;
}
void initUART(unsigned int baud, unsigned int speed)
{
unsigned int ubrr;
if(speed)
{
//double rate mode
ubrr = F_CPU/8/baud-1;
//set double speed mode
UCSR0A = (speed << U2X0);
}
else
{
//normal rate mode
ubrr = F_CPU/16/baud-1;
}
//set the baud rate
UBRR0H = (unsigned char)(ubrr >> 8);
UBRR0L = (unsigned char)(ubrr);
//enable Tx and Rx pins on MCU
UCSR0B = (1 << RXEN0) | (1 << TXEN0);
//enable transmit interrupt
UCSR0B = (1 << TXCIE0);
//set control bits, 8 bit char, 0 stop, no parity
UCSR0C = (1 <<UCSZ00) | (1 <<UCSZ01);
}
My understanding is that if I wrote the first character to UDR0 (as I did in main()), this would then trigger a Transmit Complete Interrupt, and then the next byte would be transmitted via the ISR. This does not seem to work.
The code shown here compiles using gcc. Can someone offer an explanation?
The key thing to understand is that the USART has 2 separate hardware registers that are used in the data transmission: UDRn and the Transmit Shift Register, which I'll just call TSR from now on.
When you write data to UDRn, assuming no tx is in progress, it'll get moved to the TSR immediately and the UDRE irq fires to tell you that the UDRn register is "empty". Note that at this point the transmission has just started, but the point is that you can already write the next byte to UDRn.
When the byte has been fully transmitted, the next byte is moved from UDRn to TSR and UDRE fires again. So, you can write the next byte to UDRn and so on.
You must only write data to the UDRn when it is "empty", otherwise you'll overwrite the byte it's currently storing and pending transmission.
In practice, you don't usually mind about the TXC irq, you want to work with the UDRE to feed more data to the USART module.
The TXC irq, however, is useful if you need to perform some operation when the transmission has actually completed. A common example when dealing with RS485 is to disable the transmitter once you're done sending data and possibly re-enable the receiver that you could have disabled to avoid echo.
Regarding your code
Your main issue is that you're setting UCSR0B 2 times in initUART() and the second write clears the bits you just set, so it's disabling the transmitter. You want to set all bits in one go, or use a |= on the second statement.
I'm trying to set communication between esp32 (master) and stm32 (slave) over SPI. esp32 is running under micropython and sends four bytes, for example
spi.write_readinto(b'\x31\x32\x33\x34', buf)
stm32' code is here (instead of this i use SPI_InitDef.SPI_NSS = SPI_NSS_Soft;)
void SPI_Init(void) {
...
// initialize SPI slave
// for slave, no need to define SPI_BaudRatePrescaler
SPI_InitDef.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitDef.SPI_Mode = SPI_Mode_Slave;
SPI_InitDef.SPI_DataSize = SPI_DataSize_8b; // 8-bit transactions
SPI_InitDef.SPI_FirstBit = SPI_FirstBit_MSB; // MSB first
SPI_InitDef.SPI_CPOL = SPI_CPOL_Low; // CPOL = 0, clock idle low
SPI_InitDef.SPI_CPHA = SPI_CPHA_2Edge; // CPHA = 1
SPI_InitDef.SPI_NSS = SPI_NSS_Hard; // use hardware SS
SPI_InitDef.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64; // APB2 72/64 = 1.125 MHz
SPI_InitDef.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitDef);
SPI_Cmd(SPI1, ENABLE);
NVIC_EnableIRQ(SPI1_IRQn);
//Тут мы разрешаем прерывание по приему
SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);
}
void main() {
/* Setup SysTick Timer for 10ms interrupts */
if (SysTick_Config(SystemCoreClock / 100))
{
/* Capture error */
while (1);
}
/* Configure the SysTick handler priority */
NVIC_SetPriority(SysTick_IRQn, 0x0);
SPI_Init();
while(1) {
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
for (u8 i=0; i<4; i++) {
printf("0x%02x ", SPI_I2S_ReceiveData(SPI1));
}
printf("\r\n");
}
}
But when I send four bytes 0x31 0x32 0x33 0x34 (analyzer confirms bytes were sent) and my stm gets only 0x31 0x32 0x31 0x32
UPD
I use std periph library and SPI_I2S_ReceiveData is a native method to read byte from SPI.
uint16_t SPI_I2S_ReceiveData ( SPI_TypeDef * SPIx )
Returns the most recent received data by the SPIx/I2Sx peripheral.
Parameters:
SPIx,: To select the SPIx/I2Sx peripheral, where x can be: 1, 2 or 3 in SPI mode or 2 or 3 in I2S mode or I2Sxext for I2S full duplex mode.
Return values:
The value of the received data.
uint16_t SPI_I2S_ReceiveData ( SPI_TypeDef * SPIx )
Returns the most recent received data by the SPIx/I2Sx peripheral.
Parameters:
SPIx,: To select the SPIx/I2Sx peripheral, where x can be: 1, 2 or 3 in SPI mode or 2 or 3 in I2S mode or I2Sxext for I2S full duplex mode.
Return values:
The value of the received data.
But maybe I exit out from IRQ before all data are read. I found to run the while loop until the transmission of the last byte is complete
I think the following code is not correct (but I don't know what the function SPI_I2S_ReceiveData is doing):
while(!SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE));
for (u8 i=0; i<4; i++) {
printf("0x%02x ", SPI_I2S_ReceiveData(SPI1));
}
You exit from the while as soon as one byte is ready to be read. I assume SPI_I2S_ReceiveData is only reading the SPI FIFO. in that case you try to read 4 bytes when possibly only one or two has been received.
You didn't precise the kind of STM32 you're using so I am describing the SPI of STM32H7 (as far as I know it should be pretty similar in other STM32).
To setup a reception in slave mode you should define in particular these 3 parameters:
the length of the socalled "frame" (number of bytes to be read/written at once). This is the field SPI_DataSize` in the HAL data structure, here 8 bits.
the number of transfer (TSIZE) which specifies when the End Of Transmission event is generated. It is expressed in number of "frames". This parameter must be set through register SPI.CR2 before each reception (provided you know the number of bytes to be received of course).
the "FIFO threshold". It specifies at which frequency an event RXP or TXP is generated. You can change this parameter to decrease the workload on the software but to receive only 4 bytes it has no impact.
In your case I think you should setup a transfer size of 4 (4 bytes) and wait for EOT flag to be set. When it is set you only have to read 4 bytes from SPI Receive Register (you can read all 4 bytes at once by the way).
I suggest you do not use the HAL but write your own SPI reception / transmission routines by reading / writing registers. It is not a very complex peripheral (so it will not cost you a lot of time) and you will understand precisely how it works (instead of digging into the HAL).
So I'm having a problem getting a TI microcontroller to communicate with the Raspberry Pi B+. The exact microcontroller I'm using is the TI cc430f5137. The issue I'm having is that I just can't seem to get the Raspberry Pi to correctly receive the data I'm sending from the MSP430. For those who don't know, the 430 has 2 buffers for this purpose, a RX and TX, which allows the use of the UART module while code is still executing. I've enabled an interrupt for when I receive a byte, and I simply set a flag and send the same byte right back. It works up until I attempt to transmit.
The code sits and waits in an infinite loop until it receives it's first byte. At that point it simply saves the byte and flashes the LED if it's a 'T' (for testing). Upon returning to the loop, it detects that the saved byte has changed, and puts it in the buffer to send it back. Until this point, everything works perfectly. It receives the correct byte every time, letting me know my clocks are perfect, my interrupt is working, and my UART initialization is correct. Where it goes wrong is after sending the byte, it seems like there is some kind of internal loopback (this is an option but I made sure this is not the case) that is causing the interrupt to re-trigger, resulting in an infinite loop of transmitting and again receiving the same byte, but upon invoking this via the Pi I don't get back a loop of the same character, but instead a byte of random garbage that has no consistency or logic behind it. I analyzed the bits to see if the timing is just off and that doesn't seem to be the case. For reference, my Baud is a measly 1200, the voltage of both devices is definitely 3.3v, and I'm sure the Pi is working because when I short the RX and TX, I get back the byte without an issue. I switched to UART because SPI was giving me similar problems, and I can't think of any other protocol besides I2C that would help here. I am using an external 32768hz crystal. Also, I've tried this on two different microcontrollers, so its definitely the code that's the issue.
#include <msp430.h>
char temp;
char in;
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
P1OUT = 0x00; // Make sure pins are tturned off
P1DIR = 0x01; // Led out
P1SEL |= BIT5 + BIT6; // UART as pin mode
UCSCTL6 &= ~BIT0; // Turn on XT1
P5SEL |= BIT0 + BIT1; // Select XT1 as pin function
UCA0CTL1 |= BIT0; // Set UART to reset mode
UCA0CTL1 |= BIT6; // Choose ACLK as source
UCA0BR0 = 27; // Set speed to 1200 Baud
UCA0MCTL = 0x02 << 1; // Set speed to 1200 Baud
UCA0CTL1 &= ~BIT0; // Turn UART on
UCA0IE = BIT0; // Enable RX interrupt
__enable_interrupt();
while(1)
{
if(in != 0)
{
UCA0TXBUF = in;
temp = in;
in = 0;
}
}
}
#pragma vector=USCI_A0_VECTOR
__interrupt void UCSIA0(void)
{
in = UCA0RXBUF;
if(in == 0x54)
P1OUT ^= BIT0;
}
Output from running minicom at 1200 on Pi, Sending 'T' one at a time:
UÔÿÿïÕuU_þýÿÿÿÿÿÿÕԯÿÿôÕüÿÝUõï\þþÿÿÕ¿ÿÿýýTÿýUÿÿÿïÿÿÿõÿýýÿõûÿ
assuming Pi is working currectly...
1.verify msp430 TX is woring: send every 1 sec known value and see if PI getting it currectly.
2.verify MSP430 RX working: send from Pi known value every 1 sec.
3.interrupt section:
your code dosent verfiy that RX interrupt is off.
you should filter interrupts generated only for the RX .
also, your code dont handle overrun/frame errors.
sharing "in" variable both for TX and RX (and both at interrupt and main loop section)-not good idea..
4.your output example suggests that you have baud rate mismatch issue.
if you send character 'T' and shoud get back 'T'. i expect to see 'TTTTTT...'
BTW this garbage may suggests that you forgot to connecting GND line between two MCUs...
I am trying to read analogic signal for a sort of mouse with a pic18f14k50 controller. Here the simple circuit: http://dl.dropbox.com/u/14663091/schematiconew.pdf . I have to read analogic signal from AN9 circuit port. Main function reads from the port, and blinks 30 time if threshold is reached:
void main(void) {
InitializeSystem();
#if defined(USB_INTERRUPT)
USBDeviceAttach();
#endif
while(1) {
if((USBDeviceState < CONFIGURED_STATE)||(USBSuspendControl==1)) continue;
if(!HIDTxHandleBusy(lastTransmission))
{
int readed = myReadADC2(); //Here i tried both myReadADC2() or myReadADC1()
if(readed>40) { //If read threshold > 40, blink led 30 times
int i;
for(i=0; i<30; i++) {
Delay1KTCYx(0);
mLED_1_On();
Delay1KTCYx(0);
mLED_1_Off();
}
}
lastTransmission = HIDTxPacket(HID_EP, (BYTE*)hid_report_in, 0x03);
}//end while
}//end main
I used two method to read from the AN9 port, myReadADC() that uses OpenADC() API method:
int myReadADC(void) {
#define ADC_REF_VDD_VDD_X 0b11110011
OpenADC(ADC_FOSC_RC & ADC_RIGHT_JUST & ADC_12_TAD, ADC_CH9 & ADC_INT_OFF, ADC_REF_VDD_VDD_X & ADC_REF_VDD_VSS, 0b00000010); // channel 9
SetChanADC(ADC_CH9);
ConvertADC(); // Start conversion
while(BusyADC()); // Wait for completion
return ReadADC(); // Read result
}
and myReadADC2(), that implements manual read from the port.
int myReadADC2() {
int iRet;
OSCCON=0x70; // Select 16 MHz internal clock
ANSEL = 0b00000010; // Set PORT AN9 to analog input
ANSELH = 0; // Set other PORTS as Digital I/O
/* Init ADC */
ADCON0=0b00100101; // ADC port channel 9 (AN9), Enable ADC
ADCON1=0b00000000; // Use Internal Voltage Reference (Vdd and Vss)
ADCON2=0b10101011; // Right justify result, 12 TAD, Select the FRC for 16 MHz
iRet=100;
ADCON0bits.GO=1;
while (ADCON0bits.GO); // Wait conversion done
iRet=ADRESL; // Get the 8 bit LSB result
iRet += (ADRESH << 8); // Get the 2 bit MSB result
return iDelay;
}
Both cases doesn't works, i touch (sending analogic signal) port AN9 but when I set high threshold (~50) led don't blinks, with low threshold (~0) it blinks immidiatly when i provide power to the PIC. Maybe i'm using wrong port? I'm actually passing AN9 as reading port? Or maybe threshold is wrong? How can i found the right value? Thank you
Here the MPLAB C18 Apis http://dl.dropbox.com/u/14663091/API%20microchip%20C18.pdf .
Regarding function myReadADC2(): you need to switch ANSEL and ANSELH configs as RC7/AN9 is configured in bit 1 of ANSELH. Also call me paranoid but for the line
iRet += (ADRESH << 8);
I always like to either save it a temporary variable first or cast explicitly the value ADRESH before shifting it up:
iRet += (((UINT) ADRESH) << 8);
That way I know for sure the bits won't get lost when shifting up which has bitten me before.
Regarding function myReadADC():
OpenADC() only takes two parameters. I presume that bitfield in the third parameter field is for the analog enable (ADRESH/ADRES). I'm assuming that's handled by SetChanADC() but you may have to set ADRESH/ADRES manually. It may help to set a breakpoint in the debugger and stop after configuration is complete to make sure your registers are set appropriatley.