[AVR MEGA series 0] USART stops sending at 5 bit - Page 1
Forum section: electronics / general

author: Simon
author_rank: Unknown
timestamp: August 25, 2021, 08:00:55 am
content: I am trying to send data from USART1 on a mega1608, the set up code is just the baud rate register and the enable bit. The default char size is 8 bit but no matter what I set I only ever get 5 bits into the message before the line just returns high.

author: DavidAlfa
author_rank: Super Contributor
timestamp: August 25, 2021, 01:42:20 pm
content: Having +15000 posts, you should already know these questions should be attaching some code, we aren't fortune tellers

author: Simon
author_rank: Unknown
timestamp: August 25, 2021, 03:54:51 pm
content: As I said it's really basic:Code:void USARTn_assync_transmit_setup(uint8_t instance, uint16_t baud) {USARTn_BAUD = baud ;USARTn_CTRLC =( 0x03 );(*(volatile uint16_t *)(USART_BASE + 1*USARTn_OFFSET + 0x07)) = 0x03;USARTn_CTRLB = USART_TXEN_EN ;}void USARTn_char_send(uint8_t instance, uint8_t data_byte){USARTn_TX_DATA_L = data_byte ;}// register definitions#define USART_BASE 0x0800#define USARTn_OFFSET 0x0020#define USARTn_TX_DATA_L (*(volatile uint16_t *)(USART_BASE + instance*USARTn_OFFSET + 0x02))#define USARTn_BAUD (*(volatile uint16_t *)(USART_BASE + instance*USARTn_OFFSET + 0x08))#define USARTn_CTRLA (*(volatile uint16_t *)(USART_BASE + instance*USARTn_OFFSET + 0x05))#define USARTn_CTRLB (*(volatile uint16_t *)(USART_BASE + instance*USARTn_OFFSET + 0x06))#define USART_TXEN_EN (0x1 << 6)#define USARTn_CTRLD (*(volatile uint16_t *)(USART_BASE + instance*USARTn_OFFSET + 0x0A))#define USARTn_CTRLC (*(volatile uint16_t *)(USART_BASE + instance*USARTn_OFFSET + 0x07))// NORMAL MODE#define USART_CMODE_ASSYCRONOUS (0x0 << 6)#define USART_PMODE_EVEN (0x2 << 4)#define USART_CHSIZE_8 0x03

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2021, 04:27:15 pm
content: You have 1 16bit register in usart, yet you treat them all as 16bit registers.I would come up with a better way to deal with multiple instances of a peripheral-https://godbolt.org/z/e3crMhjoxIf you use C++, then there are nicer ways to do this, like so-https://godbolt.org/z/b4MYzs5dewhere you can use the defines already in use (USART0-3) to also 'naturally' restrict the instance number (if you do not have USART3 but use it, it will fail to compile). This is obviously minimal example code, but can easily add required things like setting up pins, portmux,, etc.Same, but complete (added pin setup code) which should spit out U's-https://godbolt.org/z/41WMxojMW

author: Simon
author_rank: Unknown
timestamp: August 25, 2021, 05:32:25 pm
content: I just find it easier to write to register addresses the way I am. I am not looking to make it totally foolproof, that is what arduino is for until it does weird stuff. My aim is to have a C file that I don't touch and a header file where I set the options required. This means that for each peripheral there is just one place to worry about finding out what the settings are and one place to set them.

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2021, 07:40:11 pm
content: Then ignore all I wrote except the part about you treating all register as if they were 16 bits.Not sure why you think the uart specific info cannot be separated from the uart source-https://godbolt.org/z/696v5Wff5fyi, all my c++ peripheral drivers are headers, and there is a global header used by all source files where I have global info which also can use a 'board' class for board specific things like uart info, pins, etc. The peripheral headers remain unchanged, and only need to deal with the global header or a board header to modify things like which uart to use, what baud, which pins, or anything else that is project specific.

author: Simon
author_rank: Unknown
timestamp: August 25, 2021, 07:57:56 pm
content: Well I'd be happy just to get 8 bits out of the little bastard

author: cv007
author_rank: Frequent Contributor
timestamp: August 25, 2021, 11:44:20 pm
content: Not sure if you understand the problem you are creating by using 'volatile uint16_t *' for every register, as you have not given any indication of 'aha, I see'.By using a volatile uint16_t* for a register define, you are stomping on adjacent (+1) registers when written, which is not a good thing.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: August 26, 2021, 02:45:43 am
content: Quote from: cv007 on August 25, 2021, 11:44:20 pmYes, this is your problem.When you are writing toCTRLB, because it is defined as auint16_t, it is clobberingCTRLCbecause that register is adjacent in memory - probably writing a zero value there. And, guess what? SettingCTRLCto zero means thatCHSIZEwill be set to 5 bits!

author: Simon
author_rank: Unknown
timestamp: August 26, 2021, 07:36:48 am
content: I see, good point. The pointer formulation of each register address was given to me on here. I just blindly use it. My understanding is that it is a 16 bit address space so it has to be a 16 bit pointer.Is the actual problem then that I am not type casting the final value I put into the register and thus generating a longer than 8 bit number when I bit shift. Or it's the 16 bit address length that also sets the value up as a 16 bit unless I make it 8 bit?

author: HwAoRrDk
author_rank: Super Contributor
timestamp: August 26, 2021, 08:55:50 am
content: Quote from: Simon on August 26, 2021, 07:36:48 amYes, that's correct. AVRs do have a 16-bit address space and so pointers are indeed 16-bit. But the length of a pointer itself is not to be conflated with the length of the data it is pointing to. If you have pointers touint8_toruint32_tvalues, the pointer is 16 bits for both, but the length of the data being pointed to in those cases is 8 and 32 bits respectively.It is rare (impossible in C?) that you get to specify what the size of a pointer is - that is a characteristic of the target platform. For AVR it's 16-bit, for the likes of STM32, SAM, etc it is 32-bits, for Intel/AMD x64 PCs it is 64-bits.Quote from: Simon on August 26, 2021, 07:36:48 amThe problem is that in those register defines, you're declaring that the data beingpointed tois 16 bits. Thus, no matter what value you assign to the registers (e.g. whether it is only a small value constrained to 8-bits), the compiler will generate instructions to copy two bytes to that memory address. Where you do something likeCTRLC = 0x03, the compiler will 'pad' it out to 16 bits with zeroes (e.g.0x0003). Thus, in the case of the USART registers, assigning toCTRLCwill clobberCTRLDbecause it is at the next location in memory.You need to change your register defines to:(*(volatile uint8_t *)(USART_BASE + instance*USARTn_OFFSET + 0x02))

author: Simon
author_rank: Unknown
timestamp: August 26, 2021, 09:01:48 am
content: OK, I got it, the size is the register size not the address size. Doing it with the inbuilt register defines works. They are in fact 8 bit pointers as you say.

author: Simon
author_rank: Unknown
timestamp: August 26, 2021, 09:09:16 am
content: Now I understand why the TCA counter has separate register defines for single and split modes, it's so that the counter, period and compare channel registers can be defined in 8 or 16 bit.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: August 26, 2021, 06:11:19 pm
content: BTW, it seems like you're missing the includes. Those should be in here:http://packs.download.atmel.com/You want "Atmel ATmega Series Device Support (1.7.374)". Download the file, rename to ZIP (or open in something that doesn't care about extension) and there's your includes and everything, extract to the proper locations.Tim

author: Simon
author_rank: Unknown
timestamp: August 26, 2021, 06:42:16 pm
content: which includes? the chip includes are there as register names are recognised and the interrupts all work. I just prefer to define them myself sometimes.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: August 27, 2021, 12:11:59 am
content: I mean, sure you can, but this thread illustrates exactly the problem, right? Your in-line defines aren't generated from the hardware description (in, uh, whatever way they do it, but I assume they use automated scripts -- they aren't without errors, but when they occur they tend to be of a certain sort), and either you're lacking the proper headers and includes (just#include <avr/io.h>), or you refuse to use them and you're a victim of your own, stubbornness I guess. So, Idunno which impression you want me to gather from this, but... y'know?Tim

author: Simon
author_rank: Unknown
timestamp: August 27, 2021, 07:36:30 am
content: If as you say the headers are automatically generated that would explain why it's often impossible to track down what some registers are called. For this reason I ended up bypassing them. I then realised that it was easier to write functions that could call a specific instance of a peripheral using a number in the functions argument like the write to serial port I have here as how you turn a number into the text that represents that peripherals define text eludes me.Now that I have moved on a bit and am maturing in my coding I have come to the conclusion that setting up peripherals should be done with macro's when I define the peripheral instance as active. This does make it all a bit easier and it's possible to once again use the predefined headers as I don't mind so much repeating the code and then using #ifdef's to include the code of that instance if required.Obviously I have learnt here and that is in itself valuable.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: August 27, 2021, 12:57:28 pm
content: Yeah the naming conventions are weird at times. And they don't call them out in the datasheet like they did with the classic MEGAs, not a code sample in sight. I've found myself flipping through the IO header many times myself. Often, including it in the project so the IDE parses it, does the job (auto-complete / CTRL+SPACE lookup thing) but sometimes I can't guess the first letters and have to go searching, by address for example.In very rare cases, either it's completely missing from the header, or extra features are listed that aren't physically present in the chip. In that case I make a note of them in my own patch header, following naming conventions as well as I can (often, a missing peripheral can simply be copy-pasted from a higher-tier part), and undefining the extraneous entries.Microchip probably also wants users to move away fromanachronisticclassic development practices, into using configurator/code generator tools.Tim

author: Simon
author_rank: Unknown
timestamp: August 27, 2021, 05:33:35 pm
content: I don't really care what the names are, I just want to know what they are.I don't really care for the automated tools. Either it's something that is properly abstracted or it's bare metal. From my attempts at any of the auto config tools you get know instructions on how to drive the peripherals and have to understand how they work anyway. once set up and as the code is auto generated it's all gibberish. So given all that it's less effort to set the peripheral up yourself.