I want to learn how to use the arduino and C to show some stuff on a little LCD screen. I am planning to use a lot of Claude as well as arduino and atmega datesheets to learn all this. I am trying to learn this as a pretty low level which is why I am writting all of this in C. I think a good place to start is to just revisit the blink example, I have this many times but its always a good idea to refresh and make sure I understand even the most basic of examples. # Blink Example ```c #include <avr/io.h> #include <stdbool.h> #include <util/delay.h> #define F_CPU 16000000UL int main(void) { DDRB |= (1 << DDB5); while (true) { PORTB |= (1 << PORTB5); _delay_ms(500); PORTB &= ~(1 << PORTB5); _delay_ms(500); } return 0; } ``` Let's break this example down. The first things that jump out as weird are those constants. The first, `DDRB`, comes from the `avr` header files, and it stands for Data Direction Register B. That is, it's the register that allows us to control the direction (input/output) of the pins on port B. In this case, we would like to set the direction of the pin to which the LED is connected as an OUTPUT pin. We carry this out by using some bit-wise masking. This, in turn, takes us to the next constant, `DDB5`. `DDB5` literally just stands for the number 5, and we are saying we are going to shift the 1 (`00000001`) to the left (`<<`) by 5, which in turn gives us the number `00100000` or, in decimal, 32. We can now interpret that first bit of code as ```c DDRB = DDRB | (00100000) ``` and so what results is that we are making sure that the 6th bit of the data direction register for port B is set to ON (1), thus making it an output pin. This 6th bit of this register controls the in/out of `PB5` or `D13`, which is the pin to which the LED is connected. ![[Pasted image 20241116220252.png]] The next bit of code is all within an infinite while loop. All this part of the code does is turn on, wait, turn off, wait, and loop over and over again. Let's go over this part of the code now. We kind of just use the same "trick" we used in the previous part here as well, except we are doing it not for the register but instead for `PORTB`: ```c PORTB |= (1 << PORTB5); ``` This expands the same way as the other part of the code to: ```c PORTB = PORTB | (1 << 5) PORTB = PORTB | (00100000) ``` This last step again sets the bit to ON and therefore turns on the LED. The next part is a little more interesting but basically the same again: ```c PORTB &= ~(1 << PORTB5); ``` lets expand it the same way we did before: ```c PORTB &= ~(1 << PORTB5); PORTB = PORTB & ~(1 << 5); PORTB = PORTB & ~(00100000); PORTB = PORTB & (11011111); // the ~ flips all the bits ``` This last bitwise AND will force all of the bits to remain unchanged EXCEPT the 6th bit; it will become 0 or OFF. And that is basically it; this will toggle the light on and off on the Uno. We could go back and make everything more barebones, by removing the dependency on these constants, to do that we need to consult the datasheet and determine the address for each of these pins. The part of the datasheet that we interested in is this: ![[Pasted image 20241116224652.png]] Ok so we can see that the address of `PORTB` and `DDRB` are `0x25` and `0x24` respectively, and so the updated version of the file: ```c #include <stdbool.h> #define F_CPU 16000000UL #include <util/delay.h> #define DDRB_ADDRESS (*(volatile uint8_t *)0x24) #define PORTB_ADDRESS (*(volatile uint8_t *)0x25) int main(void) { DDRB_ADDRESS |= (1 << 5); while (true) { PORTB_ADDRESS |= (1 << 5); _delay_ms(500); PORTB_ADDRESS &= ~(1 << 5); _delay_ms(500); } return 0; } ``` The only piece we really need to explain: ```c (*(volatile uint8_t *)0x25) ``` Again, it's easier to break this down into parts. The first thing we do is cast. We cast the literal hex value `0x25` to a pointer of type `uint8_t *`, which is a pointer to an unsigned integer of size 8 bits. We tag this as `volatile` since we want to tell the compiler not to perform any optimizations—basically instructing that every time it encounters it, it should read from memory because its value may have changed. Lastly, we dereference the casted pointer. The dereference at the end gives us access to the value stored at the memory location which is exactly PORTB data as was listed in the avr header files.