I'm pretty new to programming in C, but getting used to registers and the way communication in C works. Since UART using the official Arduino read() / write() creates a high delay in passing commands through, I tried to translate this Arduino sketch into pure C. (and because I've time to play around, haha)
My ebike's controller and display are communicating using UART. I tried to read the commands and react to speed changes or brake signals, but first of all I need to get rid of this huge delay in updating the display.
I'm using an Robodyn Mega 2560 PRO (Embed), which has 4x hardware serial ports. In the first step I tried to read what's coming in and forward it to the other port. Shouldn't be to hard to implement in C, right?
void setup() {
Serial1.begin(1200); // BBSHD controller #RX18 TX19
Serial2.begin(1200); // Display DP-C18 #TX16 RX17
}
void loop() {
if (Serial2.available()) {
Serial1.write(Serial2.read());
}
if (Serial1.available()) {
Serial2.write(Serial1.read());
}
}
That's what I programmed in Atmel Studio 7 so far. I used the C example from ATmega640/1280/1281/2560/2561 - Complete Datasheet (Search for USART_Receive and USART_Transmit) and this guide Simple Serial Communications With AVR libc
Currently I can compile and flash it the 2560, but the communication is not being forwarded. I don't know what the default settings are, that Arduino with Serial uses. Which mode, number of stop bits, .. Is there anything obvious I'm missing?
#define F_CPU 16000000UL
#define BAUD 1200
#include <avr/io.h>
#include <stdio.h>
#include <util/setbaud.h>
void uart_init(void) {
/* Serial1 controller */
UBRR1H = UBRRH_VALUE;
UBRR1L = UBRRL_VALUE;
/* Serial2 display */
UBRR2H = UBRRH_VALUE;
UBRR2L = UBRRL_VALUE;
/* 8-bit data, 1stop bit*/
UCSR1C = (1<<UCSZ11) | (1<<UCSZ10);
UCSR2C = (1<<UCSZ21) | (1<<UCSZ20);
/* Enable RX and TX */
UCSR1B = (1<<RXEN1) | (1<<TXEN1);
UCSR2B = (1<<RXEN2) | (1<<TXEN2);
}
int main(void) {
uart_init();
while(1) {
if (!(UCSR1A & (1<<RXC1))) { /* Only if data is received */
while (!(UCSR1A & (1<<UDRE1))); /* Wait for empty transmit buffer */
UDR2 = UDR1; /* Put data into buffer, sends the data */
}
if (!(UCSR2A & (1<<RXC2))) { /* Only if data is received */
while (!(UCSR2A & (1<<UDRE2))); /* Wait for empty transmit buffer */
UDR1 = UDR2; /* Put data into buffer, sends the data */
}
}
}
Related
in an exercise for my embedded programming course we have to program an Atmega328p AVR chip to receive data through the serial port. We have to do this by calling a function that waits until it receives a char. Next it should display the ascii value of that char in led lights, but I am having trouble even receiving it. I've done a lot of debugging and I think I narrowed it down to PuTTY not even sending the data, or the AVR not receiving it properly. I will put my code in below:
/*
From the PC should come a char, sent via the serial port to the USART data register.
It will arrive in the RXB, which receives data sent to the USART data register.
While running the loop, the program should encounter a function that is called and waits for the RXB to be filled.
Then it will read the RXB and return it to the main loop.
The result will be stored and processed accordingly.
*/
#define F_CPU 15974400
#include <util/delay.h>
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
void writeChar(char x);
void initSerial();
char readChar();
int main(void)
{
initSerial();
while (1)
{
char c = readChar(); //reads char, puts it in c
_delay_ms(250); //waits
writeChar(c); // spits that char back into the terminal
}
}
void initSerial(){
UCSR0A = 0;
//UCSR0B = (1 << TXEN0); // Enable de USART Transmitter
UCSR0B = 0b00011000; //transmit and receive enable
//UCSR0C = (1 << UCSZ01) | (0 << UCSZ00); /* 8 data bits, 1 stop bit */
UCSR0C = 0b00100110; // Even parity, 8 data bits, 1 stop bit
UBRR0H=00;
UBRR0L=103; //baudrade 9600 bij
}
void writeChar(char x){
while(!(UCSR0A & (1 << UDRE0))); // waits until it can send data
UDR0 = x; // Puts x into the UDR0, outputting it
}
char readChar(){
while (!(UCSR0A & (1 << RXC0))); //waits until it can send data
return UDR0; // returns the contents of the UDR0 (the receiving part of course
}
The problem is that when I enter anything in PuTTY (that I assume I set up correctly. https://prnt.sc/rc7f0f and https://prnt.sc/rc7fbj seem to be the important screens.
Thanks in advance, I am completely out of ideas.
I fixed it myself. I figured it out while taking it downstairs to test it on another laptop. I still had LEDs put in all pins of PORTD, all on input mode (that's the default mode). I quick look at the Atmega328p user guide (section 2.5.3) revealed that the pin D0 was actually the RxD in for the USART. By putting an LED on it and effectively grounding it, it was always pulled low, and would never be put high by the CPU, which would stop the while loop check while (!(UCSR0A & (1 << RXC0))); //waits until it can send data in readChar();
So by simply removing that led it would work again. Obviously that would mean it was floating, so I set the DDRD to all be output, as nothing needed to be input anyway.
That ended up fixing it.
So I am having a tough time setting up my SPI, and getting it to work with the LSM330 device to read accelerometer data. I'm simply setting up the SPI system, and trying to use that to read the WHO_AM_I_A register to confirm that it is working and all I'm getting back is 0xff rather than 0x40.
Here is my code:
main.c
#include <avr/io.h>
#include "LSM330.h"
#include "spi.h"
#include "CLK.h"
#include "accel.h"
int main(void) {
//set clock
set32MhzClock();
//first set SPI slave selects for accel gyro to false(high)
PORTF.OUTSET = PIN4_bm | PIN3_bm;
//set serial system of SPI
PORTA_OUTCLR = PIN4_bm;
//init SPI
spi_init();
//reset LSM330 accel by sending 0x01 to CTRL_REG4_A
accel_write(0x01, CTRL_REG4_A);
//trying to read from WHO_AM_I_A reg
uint8_t whoAreYou = accel_read(WHO_AM_I_A);
printf(whoAreYou);
}
accel.h
#include "LSM330.h"
#include "spi.h"
void accel_write(uint8_t reg_addr, uint8_t data) {
//make slave select signal LOW
PORTF_OUTCLR = PIN3_bm;
//set sensor_sel
PORTF_OUTSET = PIN2_bm;
//first write reg_addr, w/ RW=0 and MS=0
uint8_t writeResult = spi_write(reg_addr);
//then write data byte
spi_write(data);
return;
}
spi.h
void spi_init(void) {
//set correct pins as outputs and inputs
PORTF.DIRSET = PIN7_bm | PIN5_bm | PIN4_bm | PIN3_bm; //SCK, MOSI, SSG, SSA as outputs
PORTF.DIRCLR = PIN6_bm; //MISO as input
//set up SPI as master
SPIF.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_3_gc | SPI_PRESCALER_DIV16_gc;
//clear spi interrupt flag by reading spsr and spdr
SPIF_STATUS;
SPIF_DATA;
return
}
uint8_t spi_write(uint8_t dataIn) {
//write data
SPIF.DATA = dataIn;
//wait for transmission to complete
while(!(SPIF_STATUS & SPI_IF_bm));
return SPIF.DATA;
}
uint8_t spi_read(vodi) {
return spi_write(0xFF);
}
And that's all I have. Have tried many things, and can't seem to get this thing to work. It seems like SPI is working because it's not getting stuck up in any of the while loops when writing data.
Without going too much into the details, there is definitely something wrong here:
//reset LSM330 accel by sending 0x01 to CTRL_REG4_A
accel_write(0x01, CTRL_REG4_A);
But the declaration of your function is as follows:
void accel_write(uint8_t reg_addr, uint8_t data)
It seems you are setting register 0x01 to value of CTRL_REG4_A.
I'm using Arduino mega 2560 and light sensor BH1750FVI. to show result i'm using minicom, work on Ubuntu 16.04 LTS
I write code and it's work and send me data result reading from light sensor, i can read info only once. I don't understand how i make it work through interrupts (TWI_vect) and display the results every time using my uart.
#include "my_header.h"
#define I2C_STATUS_MASK 0xF8
#define START_COND_TRANSMITTED 0x08
#define REPEATED_START_COND_TRANSMITTED 0x10
#define SLA_W_TRANSMITTED_ACK_RECEIVED 0x18
#define DATA_TRANSMITTED_ACK_RECEIVED 0x28
#define SLA_R_TRANSMITTED_ACK_RECEIVED 0x40
#define DATA_RECEIVED_ACK_RETURNED 0x50
#define DATA_RECEIVED_NACK_RETURNED 0x58
volatile int light_intensity = 0; // var for read data from sensor
ISR(USART0_UDRE_vect) {
if (!bufferIsEmpty(&buffer)) //if we have something to read do it
UDR0 = popFromBuff(&buffer);
else
UCSR0B &= ~_BV(UDRIE0); //disallow interrupts
}
int main(void) {
cli();
init_port(); //initialize my port
init_buffer(&buffer, BUFF_SIZE); //init ring_buffer for uart
init_uart();
TWI_init();
sei();
TWI_start(); //send start
TWI_send_SLA(WRITE); //send SLA+W to light sensor
TWI_sendData(0b00010000); //opecode for Measurement
TWI_stop(); //stop
TWI_start(); //send start
TWI_send_SLA(READ); // send SLA+R to sensor
TWI_readData(); // read data from it light_intensity = TWDR
TWI_stop(); // stop
u_printnumbers(light_intensity); /*my func that send data to ring_buffer and later
*using interrupts send it form buffer to UDR
*/
u_print("\n");
while (1) {
if (!bufferIsEmpty(&buffer)) /*if buffer is not empty allow interrupts for uart*/
UCSR0B |= _BV(UDRIE0);
}
}
So this code work and i receive data from sensor and all fine. But i want using it with interrupts (TWI_vect) and receive data all the time from sensor like in while(). i read a lot of info but don't understand how it should look.
Can you show me the right way using my code with interrupts (TWI_vect) some little example. Thx for help!
To use the Two-Wirte interface (I²C; TWI) interrupt-driven, you will have to implement some sort of state machine inside the TWI ISR.
Atmel Application Note AVR135 give a slight idea how this can be done. You will have to have buffers ready and abstract transactions into a struct of some sort. For example, you can have a
struct i2c_transfer {
uint8_t i2c_sla; /* slave address */
const uint8_t *txbuf;
size_t txbytes;
uint8_t *rxbuf;
size_t rxbytes;
}
That you set up, then have a function start transmitting I2C and check if you're done via checking if txbytes == 0 && rxbytes == 0 or an error occurred. Your ISR would then need to check its state and act appropriately depending on the number of bytes it's supposed to write and read.
I'm currently developing a system which involves sending a request string to a sensor device connected via UART to an Atmel SAML21 Xplained Pro board. I'm testing with an Arduino board as the "sensor device", but eventually, it'll be for a Rotronic HC-2 sensor.
The process goes something like this:
MCU sends string { 99RDD} over UART to sensor
-> delay of up to 500ms
-> Response string of 99 bytes sent back via UART
-> Response transmitted to virtual com port on embedded debugger
My issue is that for some reason, I'm either not getting anything sent back, or it's sending back the variable request_msg
I know that the response from the sensor should be 99 bytes of ASCII, and I've tested both the actual sensor, and the Arduino test board over serial connectors to ensure that the readings are coming back correctly.
The software is using Atmel ASF v4.0, which is great when it works, but the documentation is fairly flaky, so I was hoping someone with more experience could point me as to where I'm going wrong in the code.
I have the following code for my main application:
#include "atmel_start.h"
#include "atmel_start_pins.h"
#include <string.h>
static uint8_t example_hello_world[14] = "Hello World!\n";
static uint8_t example_error_msg[13] = "UART Error!\n";
static uint8_t request_msg[24] = "Sending Sensor Request\n";
static uint8_t rotronic_ascii[8] = "{ 99RDD}";
volatile static uint32_t data_arrived = 0;
volatile static uint32_t reading_received = 0;
static void tx_cb_EDBG_COM(const struct usart_async_descriptor *const io_descr)
{
/* Transfer completed */
gpio_toggle_pin_level(LED0);
}
static void rx_cb_EDBG_COM(const struct usart_async_descriptor *const io_descr)
{
/* Receive completed */
data_arrived = 1;
}
static void err_cb_EDBG_COM(const struct usart_async_descriptor *const io_descr)
{
/* error handle */
io_write(&EDBG_COM.io, example_error_msg, 13);
}
static void tx_cb_COM1(const struct usart_async_descriptor *const io_descr)
{
/* Transfer completed */
gpio_toggle_pin_level(LED0);
}
static void rx_cb_COM1(const struct usart_async_descriptor *const io_descr)
{
/* Receive completed */
reading_received = 1;
}
static void err_cb_COM1(const struct usart_async_descriptor *const io_descr)
{
/* error handle */
io_write(&COM1.io, example_error_msg, 13);
}
int main(void)
{
volatile uint8_t recv_char[99];
atmel_start_init();
// Setup the EDBG Serial Port
usart_async_register_callback(&EDBG_COM, USART_ASYNC_TXC_CB, tx_cb_EDBG_COM);
usart_async_register_callback(&EDBG_COM, USART_ASYNC_RXC_CB, rx_cb_EDBG_COM);
usart_async_register_callback(&EDBG_COM, USART_ASYNC_ERROR_CB, err_cb_EDBG_COM);
usart_async_enable(&EDBG_COM);
// Send a test string to ensure EDBG Serial is working
io_write(&EDBG_COM.io, example_hello_world, 14);
// Setup the Rotronic [Arduino] Serial Port
usart_async_register_callback(&COM1, USART_ASYNC_TXC_CB, tx_cb_COM1);
usart_async_register_callback(&COM1, USART_ASYNC_RXC_CB, rx_cb_COM1);
usart_async_register_callback(&COM1, USART_ASYNC_ERROR_CB, err_cb_COM1);
usart_async_enable(&COM1);
while (1) {
if (reading_received == 0)
{
// Delay for a Bit
delay_ms(5000);
// Notify the EDBG COM Port
io_write(&EDBG_COM.io, request_msg, 24);
// Send the Rotronic ASCII
io_write(&COM1.io, rotronic_ascii, 8);
}
// Check if Reading has been Received
if (reading_received == 1)
{
while (io_read(&COM1.io, &recv_char, 99) == 99)
{
// Write what's on the buffer from the receiver
io_write(&EDBG_COM.io, recv_char, 99);
}
// Reset the flag
reading_received = 0;
}
}
}
You seem to be coding for ASFv3 - v4 will trigger your receive callback for any incoming bytes, not only once when your buffer is full (and you have received every 99 characters).
That means that io_read will most probably never return 99 (because it was only a partial read of your message) and you will most probably never send anything back.
Note the docs say (Scroll down to "different read function behavior..."):
In ASFv4 a data reception type callback in a driver with a ring buffer is triggered for every received data.
The UART apparently is a driver with a ring buffer.
You need to repeatedly call io_read and sum up the number of received bytes until you have got 99. Only then proceed. The ASF docs have an example for that. Make sure you copy code from there that fits your version.
I'm more of a high level software guy but have been working on some embedded projects lately so I'm sure there's something obvious I'm missing here, though I have spent over a week trying to debug this and every 'MSP' related link in google is purple at this point...
I currently have an MSP430F5529 set up as an I2C slave device whose only responsibility currently is to receive packets from a master device. The master uses industry grade I2C and has been heavily tested and ruled out as the source of my problem here. I'm using Code composer as my IDE using the TI v15.12.3.LTS compiler.
What is currently happening is the master queries how many packets (of size 62 bytes) the slave can hold, then sends over a few packets which the MSP is just currently discarding. This is happening every 100ms on the master side and for the minimal example below the MSP will always just send back 63 when asked how many packets it can hold. I have tested the master with a Total Phase Aardvark and everything is working fine with that so I'm sure it's a problem on the MSP side. The problem is as follows:
The program will work for 15-20 minutes, sending over tens of thousands of packets. At some point the slave starts to hold the clock line low and when paused in debug mode, is shown to be stuck in the start interrupt. The same sequence of events is happening every single time to cause this.
1) Master queries how many packets the MSP can hold.
2) A packet is sent successfully
3) Another packet is attempted but < 62 bytes are received by the MSP (counted by logging how many Rx interrupts I receive). No stop condition is sent so master times out.
4) Another packet is attempted. A single byte is sent before the stop condition is sent.
5) Another packet is attempted to be sent. A start interrupt, then a Tx interrupt happens and the device hangs.
Ignoring the fact that I'm not handling the timeout errors on the master side, something very strange is happening to cause that sequence of events, but that's what happens every single time.
Below is the minimal working example which is reproducing the problem. My particular concern is with the SetUpRx and SetUpTx functions. The examples that the Code Composer Resource Explorer gives only has examples of Rx or Tx, I'm not sure if I'm combining them in the right way. I also tried removing the SetUpRx completely, putting the device into transmit mode and replacing all calls to SetUpTx/Rx with mode = TX_MODE/RX_MODE, which did work but still eventually holds the clock line low. Ultimately I'm not 100% sure on how to set this up to receive both Rx and Tx requests.
#include "driverlib.h"
#define SLAVE_ADDRESS (0x48)
// During main loop, set mode to either RX_MODE or TX_MODE
// When I2C is finished, OR mode with I2C_DONE, hence upon exit mdoe will be one of I2C_RX_DONE or I2C_TX_DONE
#define RX_MODE (0x01)
#define TX_MODE (0x02)
#define I2C_DONE (0x04)
#define I2C_RX_DONE (RX_MODE | I2C_DONE)
#define I2C_TX_DONE (TX_MODE | I2C_DONE)
/**
* I2C message ids
*/
#define MESSAGE_ADD_PACKET (3)
#define MESSAGE_GET_NUM_SLOTS (5)
static volatile uint8_t mode = RX_MODE; // current mode, TX or RX
static volatile uint8_t rx_buff[64] = {0}; // where to write rx data
static volatile uint8_t* rx_data = rx_buff; // used in rx interrupt
static volatile uint8_t tx_len = 0; // number of bytes to reply with
static inline void SetUpRx(void) {
// Specify receive mode
USCI_B_I2C_setMode(USCI_B0_BASE, USCI_B_I2C_RECEIVE_MODE);
// Enable I2C Module to start operations
USCI_B_I2C_enable(USCI_B0_BASE);
// Enable interrupts
USCI_B_I2C_clearInterrupt(USCI_B0_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
USCI_B_I2C_enableInterrupt(USCI_B0_BASE, USCI_B_I2C_START_INTERRUPT + USCI_B_I2C_RECEIVE_INTERRUPT + USCI_B_I2C_STOP_INTERRUPT);
mode = RX_MODE;
}
static inline void SetUpTx(void) {
//Set in transmit mode
USCI_B_I2C_setMode(USCI_B0_BASE, USCI_B_I2C_TRANSMIT_MODE);
//Enable I2C Module to start operations
USCI_B_I2C_enable(USCI_B0_BASE);
//Enable master trasmit interrupt
USCI_B_I2C_clearInterrupt(USCI_B0_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
USCI_B_I2C_enableInterrupt(USCI_B0_BASE, USCI_B_I2C_START_INTERRUPT + USCI_B_I2C_TRANSMIT_INTERRUPT + USCI_B_I2C_STOP_INTERRUPT);
mode = TX_MODE;
}
/**
* Parse the incoming message and set up the tx_data pointer and tx_len for I2C reply
*
* In most cases, tx_buff is filled with data as the replies that require it either aren't used frequently or use few bytes.
* Straight pointer assignment is likely better but that means everything will have to be volatile which seems overkill for this
*/
static void DecodeRx(void) {
static uint8_t message_id = 0;
message_id = (*rx_buff);
rx_data = rx_buff;
switch (message_id) {
case MESSAGE_ADD_PACKET: // Add some data...
// do nothing for now
tx_len = 0;
break;
case MESSAGE_GET_NUM_SLOTS: // How many packets can we send to device
tx_len = 1;
break;
default:
tx_len = 0;
break;
}
}
void main(void) {
//Stop WDT
WDT_A_hold(WDT_A_BASE);
//Assign I2C pins to USCI_B0
GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN0 + GPIO_PIN1);
//Initialize I2C as a slave device
USCI_B_I2C_initSlave(USCI_B0_BASE, SLAVE_ADDRESS);
// go into listening mode
SetUpRx();
while(1) {
__bis_SR_register(LPM4_bits + GIE);
// Message received over I2C, check if we have anything to transmit
switch (mode) {
case I2C_RX_DONE:
DecodeRx();
if (tx_len > 0) {
// start a reply
SetUpTx();
} else {
// nothing to do, back to listening
mode = RX_MODE;
}
break;
case I2C_TX_DONE:
// go back to listening
SetUpRx();
break;
default:
break;
}
}
}
/**
* I2C interrupt routine
*/
#pragma vector=USCI_B0_VECTOR
__interrupt void USCI_B0_ISR(void) {
switch(__even_in_range(UCB0IV,12)) {
case USCI_I2C_UCSTTIFG:
break;
case USCI_I2C_UCRXIFG:
*rx_data = USCI_B_I2C_slaveGetData(USCI_B0_BASE);
++rx_data;
break;
case USCI_I2C_UCTXIFG:
if (tx_len > 0) {
USCI_B_I2C_slavePutData(USCI_B0_BASE, 63);
--tx_len;
}
break;
case USCI_I2C_UCSTPIFG:
// OR'ing mode will let it be flagged in the main loop
mode |= I2C_DONE;
__bic_SR_register_on_exit(LPM4_bits);
break;
}
}
Any help on this would be much appreciated!
Thank you!