|
TTK4155 Lab Node 1
Term project in the NTNU course TTK4155 Embedded and Industrial Computer Systems Design
|
Repo containing code and infrastructure for building the semester project in the NTNU subject TTK4155 Embedded and Industrial Computer Systems Design
Picocom (use tab-completion for the serial device): picocom /dev/<SERIAL_DEVICE> --baud 9600 --parity n --databits 8 --stopbits 1
The shortened version picocom /dev/<SERIAL_DEVICE> -b 9600 -y n -d 8 -p 1
We were sending the letter 'k' but recieving 'J'. Using a scope on the TX output of the uC showed the correct databits, but after the MAX233 we were reading something else.
We were setting the wrong baud rate and the synchronisation issues revealed themselves later down the chain of components. Turns out, you should set both UBRRH and UBRRL regardless of the size of the UBRR number.
While all our addresses were passing the write test, half were failing on read. We figured it might be caused by a single bit not flipping and thus the addresses using the faulty bit were being overwritten. Tried using known good code, redid the wiring a dozen times, stuck decoupling caps everywhere and swapped out both the latch and uC, all to no avail.
We probed again and noticed a single bit not latching. While all other outputs on the latch were switching between 0V and 3.5V (nominal according to the datasheet), the faulty one switched between 3.5v and 5v, leading to reading a constant logic high. Strange considering this was our sixth latch so the odds of it being faulty were astronomically small.
One of the breadboard holes between the uC and had a small particle stuck inside. While the contacts for both the uC and latch were good, this particle likely shorted two paths causing noise and undefined behaviour. Angrily stabbing the offending hole with a dupont wire fixed the issue.
Our early attempts at getting SPI up and running were met with a single packet read on the oscilloscope, before code execution got stuck in the SPI transmission done loop.
As the uC supports slave mode as well, it features a SS pin. If triggered, the uC will automatically enter slave mode, regardless of register config. In order to avoid accidentally pulling the wrong logic on this, it can either be pulled HIGH or explicitly defined as output. The latter allows us to keep using the pin as a output, hence proving the better solution.
While this lab is based around a 4.915200MHz crystal, there was an attempt at using a 16MHz crystal as well. After adjusting the fuses to support an 8+MHz external clock the code was acting strange. A delay was only lasting an eighth of the time passed to the function. Setting the clock-divider fuse solved it, but caused issues for the UART baudrate.
The delay library has a software defined macro F_CPU defining the clock speed. We´ve been lucky this is compatible with our usual clock, but now it had to be defined from the compiler. Adding -DF_CPU=<clock> to the compilation command solved it (see commit #7f15e61).
Halfway through initialising the CAN controller, the entire MCU froze. Upon probing the SPI connection to the controller, the clock line seemed to stop at arbitrary points while MOSI was still transmitting.
The spi_write_n was causing problems. It implicitly passes NULL as the output buffer and trashes whatever the SPI device responds with when sending. When sending n bytes, the function indexes into the output buffer in order to store the data. Indexing past NULL-ptr accesses and overwrites important system- memory and causes undefined behaviour. This is equivalent to a segmentation fault on an operating system and was fixed by only writing the output buffer if not NULL.
When first implementing CAN on node 1 we had problems with the TX-buffer filling. After sending one message it froze and reported full buffer.
We forgot to read the CAN frame we were sending...
After switching from loopback to normal mode we were finally reading messages on the TX-bus. The messages however, were all overload frames.
CAN-bus is a network and requires at least two nodes to acknowledge eachother's messages.
We were sending frames about 6.5 times a second from Node 1. Node 2, however was only receiving one every 5 seconds. We verified the connection, and with an oscilloscope we noticed most of the frames got NACK.
When testing the driver we implemented both polling and interrupt CAN. Turns out, these interfere with each other and cause problems in the long run. Switching to interrupt only solved the problem.
After getting the PWM up and running the duty-cycle was inverted. Not thinking twice we inverted the result of the equation and went on our merry way. When integrating the motor, the PWM signal was acting up whenever we switched the duty-cycle to something other than alternating between two values. Changing the period was especially troublesome. It went as far so as to act differently depending on flashed, when we flashed and what the Arduino had for dinner the night before.
When setting the DTY and PRD we were, out of habit, OR-ing the registers. This is obvious in hindsight, but means our values would be OR-ed together with whatever garbage is in the registers when initialised. Changing this to regular = solved the problems.
The motor encoder was only reading 0, despite manual movement of the motor.
We were writing to the wrong register, thus setting the incorrect clock speed.
We had persistent issues with node 1 crashing and freezing at inconvenient places. At first it seemed to be an issues with the CAN driver, as the node worked when removing CAN. However, when adding LOG statements to figure it out, the same problem arose.
By chance we noticed the memory section in Microchip Studio. This reported 98% data memory and 30-something% program memory. As most of the group were flashing using avr-gcc and avrdude we configured the makefile to run avr-size on the binary after each flash. Sure enough, we had completely filled the internal memory of the MCU. Of course, we could add a linker script for mapping the external SRAM and assign an attribute to some of the larger structs and buffers. Instead, however, we opted to buffer most of the already known strings used by our logging module to the PROGMEM. This freed up most of our memory and we're now diligently keeping and eye on the ram of our controller.