Maybe this question will be kinda stupid, but I wonder why
if we want to write 0 on some specific position in register
we have to write like this:
PORTB &= ~(1 << 2);
And not like this (cause it will not work):
PORTB &= (0 << 2);
Does 0 mean here like 0b00000000 (for 8-bit register)?
Let’s break this down step by step:
// PORTB &= ~(1 << 2);
int bit_position = 2;
int bit_mask = 1; // = 0000 0001
bit_mask <<= bit_position; // = 0000 0100
int inverted_mask = ~bit_mask; // = 1111 1011
int PORTB = 0x77; // 0111 0111
PORTB &= inverted_mask; // & 1111 1011
// = 0111 0011
And, your suggested alternative:
// PORTB &= (0 << 2);
int bit_position = 2;
int bit_mask = 0; // = 0000 0000
bit_mask <<= bit_position; // = 0000 0000
int PORTB = 0x77; // 0111 0111
PORTB &= bit_mask; // & 0000 0000
// = 0000 0000
The point being that the computer simply performs the operations one by one and on whole registers. It doesn’t know that it was supposed to care about a specific zero out of all the zeroes in that number. So you are not ANDing by “zero at position 2”, but simply by the number zero. And indeed, you are not shifting just one zero or one, but the entire number.
This is why to set a specific bit to zero, we have to AND with a mask that has ones in every position other than the one we wish zero. (ANDing with 1 does not change the bit, whereas ANDing with 0 makes it zero.)
Using a byte for simplicity, 0 << 2 is just equal to zero - 0b00000000. Any value you AND with this will also produce zero as a result.
1 << 2 is equal to 0b00000100
~(1 << 2) is equal to 0b11111011
Now you have a mask to AND with which will only set a specific bit to zero. The rest of the bits will be unchanged.
You can understand why, when you disassemble the code:
PORTB &= ~(1 << 2);
is translated like that:
PORTB = PORTB & (~(0b00000001 << 2));
to
PORTB = PORTB & (~(0b00000100));
to
PORTB = PORTB & ((0b11111011));
This means it will take the old value of PORTB and just write bit #2 of it to 0 while keeping the rest of the bits to their original values (whether they are 0 or 1)
This is done because you only want to change the value of PIN2 of PORTB while not changing any other pin value.
While the other line:
PORTB &= (0 << 2);
evalutes to
PORTB = PORTB & ((0b00000000));
Which in turn will reset all the PORTB pins to 0.
So it really depends on what exactly you want to do.
Another example is if you want to set PIN2 of PORTB to 1:
PORTB = PORTB | (0b00000100);
which can be written like that:
PORTB |= (1 << 2);
This will make sure to set only PIN2 and no other pin of PORTB to 1.
Related
I use Atmega328p Timer0 and its two OCRs:
void timer0_ini(void)
{
TCCR0A = 0;// set entire TCCR0A register to 0
TCCR0B = 0;// same for TCCR0B
TCNT0 = 0;//initialize counter value to 0
OCR0B = 125; // OCR0B is less than OCR0A, so it works
OCR0A = 250;
TCCR0A = (0<<COM0A1)|(0<<COM0A0)|(0<<COM0B1)|(1<<COM0B0)|(1<<WGM01)|(0<<WGM00);
TCCR0B |= (0<<FOC0A)|(0<<FOC0B)|(0<<WGM02)|(1<<CS02)|(1<<CS00);
TIMSK0 = (1 << OCIE0A)|(1 << OCIE0B);
}
ISR (TIMER0_COMPB_vect)
{
PORTB ^= (1 << PORTB2);
}
ISR (TIMER0_COMPA_vect)
{
PORTB ^= (1 << PORTB1);
}
It works only when OCR0B < OCR0A.
I can't understand why it makes sense.
Using 0, 1, 0 for WGM02, WGM01, WGM00, you enabled CTC (Clear Timer on Compare match mode) — TCNT0 register will be cleared after it reaches OCR0A value so that OCR0B value will never be reached if it is higher than OCR0A.
Updated to answer the questions in comment:
Yes, it is possible to disable clearing of TCNT0. Just set WGM0x bits to 0. See table Waveform Generation Mode Bit Description at end of TCCR0A description.
No, a top value for the TCNT0 register is OCR0A or fixed 0xFF, see the same table.
It is not clear from your question what do you want to do. Perhaps, one can place the larger value into OCR0A and the smaller into OCR0B and set a flag that says which interrupt should change which port bit.
If you want to make two independent frequencies, set timer in free-running mode (all WGM bits equal 0) and try
ISR (TIMER0_COMPB_vect)
{
PORTB ^= (1 << PORTB2);
OCR0A += 125;
}
ISR (TIMER0_COMPA_vect)
{
PORTB ^= (1 << PORTB1);
OCR0B += 250;
}
I'm tinkering around with an ATMega328P right now and wanted to read an analogue value from a pin through the ADC and simply output the value to 4 LEDs. Really simple
#define F_CPU 20000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BRIGHTNESS_PIN 2
#define ADC_SAMPLES 5
void init_adc()
{
//set ADC VRef to AVCC
ADMUX |= (1 << REFS0);
//enable ADC and set pre-scaler to 128
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
}
uint16_t read_adc(unsigned char channel)
{
//clear lower 4 bits of ADMUX and select ADC channel according to argument
ADMUX &= (0xF0);
ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
//start ADC conversion
ADCSRA |= (1 << ADSC);
//wait for conversion to finish
while(!(ADCSRA & (1 << ADIF)));
ADCSRA |= (1 << ADIF); //reset as required
return ADC;
}
int main(void)
{
uint32_t brightness_total;
uint16_t brightness = 0;
uint32_t i = 0;
init_adc();
sei();
while (1)
{
//reset LED pins
PORTB &= ~(1 << PINB0);
PORTD &= ~(1 << PIND7);
PORTD &= ~(1 << PIND6);
PORTD &= ~(1 << PIND5);
PORTB |= (1 << PINB1); //just blink
read_adc(BRIGHTNESS_PIN); //first throw-away read
//read n sample values from the ADC and average them out
brightness_total = 0;
for(i = 0; i < ADC_SAMPLES; ++i)
{
brightness_total += read_adc(BRIGHTNESS_PIN);
}
brightness = brightness_total / ADC_SAMPLES;
//set pins for LEDs depending on read value.
if(brightness > 768)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
PORTD |= (1 << PIND6);
PORTD |= (1 << PIND5);
}
else if (brightness <= 768 && brightness > 512)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
PORTD |= (1 << PIND6);
}
else if (brightness <= 512 && brightness > 256)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
}
else if (brightness <= 256 && brightness >=64)
{
PORTB |= (1 << PINB0);
}
_delay_ms(500);
PORTB &= ~(1 << PINB1); //just blink
_delay_ms(500);
}
}
This works kind of fine, except the channel selection. When I select a channel it works fine, but independently from the selected channel, channel 0 also always reads and converts. What I mean with that is that if I plug the cable into the selected channel pin, it reads the values correctly. When I plug it into any other channel pin it obviously doesn't, except for ADC0. No matter what channel I set, not only does that one read but also ADC0.
Why is that and how do I fix that?
I already checked my PCB for solder bridges, but there are none and I would also expect some slightly different behaviour with that.
Also ADC4 and ADC5 don't seem to properly convert either. Any idea why that is? The only clue I found in the datasheet is, that those two use digital power, while all the other ADCs use analogue. What's the difference, why does it matter and why does it not correctly convert my anlogue signal?
Both ARef and AVCC are connected according to the datasheet, with the exception that the inductor for ARef is missing.
I think what is happening is that
ADMUX &= (0xF0);
is setting the channel to 0, and
ADMUX |= (channel & 0x0F);
is then setting the channel to the one you want. You're then taking a reading and throwing the result away, which should mean that the initial channel being set to 0 doesn't matter.
Howevever, when you then try to take an actual reading, you are setting the channel again, by using read_adc to take the reading. So, you don't ever throw a reading away.
What I would do is replace your ADMUX setting commands with:
ADMUX = (0xF0) | (channel & 0x0F)
Then move this into a separate function called something like set_adc_channel(int channel). Include a throw away read in that function, then remove the ADMUX setting from your read_adc function. Just start a conversion and get the result.
Also note that since you only ever use one channel, you could move the channel setting part to init_adc(). I assume it's in a separate function so you could later read more than one channel.
I hope that's clear. Let me know if not.
EDIT: So as you stated, ADIF is really reset by writing logic 1.
I've just tested your adc_read function and it is working for me (if you don't mind Arduino mixture)
uint16_t read_adc(unsigned char channel)
{
//clear lower 4 bits of ADMUX and select ADC channel according to argument
ADMUX &= (0xF0);
ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
//start ADC conversion
ADCSRA |= (1 << ADSC);
//wait for conversion to finish
while(!(ADCSRA & (1 << ADIF)));
ADCSRA |= (1 << ADIF); //reset as required
return ADC;
}
void setup() {
Serial.begin(57600);
//set ADC VRef to AVCC
ADMUX |= (1 << REFS0);
//enable ADC and set pre-scaler to 128
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
}
void loop() {
Serial.println(read_adc(0));
Serial.println(read_adc(1));
Serial.println(read_adc(2));
Serial.println(read_adc(3));
delay(1000);
}
I just connect one of these channels to 3.3V pin and it'll read 713 on it. Other channels are pulled up to levels about 1017.
I'm attempting to learn how to program micro-controllers in C and have a question regarding bit assignments. Say for example I were to declare an 8 bit number.
binary_number = 0b00000000;
Now, lets say I wanted to set bit 3 only. Example texts I have seen use an operation like the following:
binary_number |= (1<<4)
Am I understanding this correctly? We are taking binary_number and 'or-ing' it with essentially 0b00001000 and then assigning that outcome to binary_number?
Likewise, when resetting this bit:
binary_number &= ~(1<<4)
We are essentially taking binary_number (0b00001000) and 'and-ing' it with 0b11110111 and then assigning binary_number to the outcome of that expression?
Do I understand this correctly?
Yes, your understanding is correct! :)
but a little change...
For resetting or setting the bit 3, you need to left shift the 1 by 3 places only.
1<<4 : 0b00010000
1<<3 : 0b00001000
Use the bitwise OR operator (|) to set xth bit.
n |= 1 << x;
That will set bit x.
Use the bitwise AND operator (&) to reset xth bit.
n &= ~(1 << x);
That will reset bit x.
Yes, you are understanding it correctly. That is exactly what you have to do to set and unset bits. Looks pretty complicated I know but you could always write a helper function that you pack around with you.
As pointed out others, you are setting/resetting the 4th bit not the 3rd
Set mask: 1<<4 = 10000 = 0001 0000
Reset Mask: ~(1<<4) = ~(10000)= ~(0001 0000) = 1110 1111
So, binary_number |= (1<<4):
Original Number - 0000 0000
Mask to set 4th bit - (OR) 0001 0000
--------
Result 0001 0000 //4th bit set
And, binary_number &= ~(1<<4):
Original Number - 000X 0000 //4th bit could be a 1 or a 0; X =1/0
Mask to set 3rd bit - (AND) 1110 1111
--------
Result 0000 0000 //4th bit reset
If you seek efficiency the best way to do it is to define the bits individually:
#define BIT_0 0b00000001
#define BIT_1 0b00000010
#define BIT_2 0b00000100
#define BIT_3 0b00001000
#define BIT_4 0b00010000
#define BIT_5 0b00100000
#define BIT_6 0b01000000
#define BIT_7 0b10000000
and then to set the bit in a byte:
unsigned char byteWhoseBitsAreSetReset = 0;
//To Set bit 3
byteWhoseBitsAreSetReset |= BIT_3;
//To Set Multiple bits
byteWhoseBitsAreSetReset |= (BIT_3 + BIT_4 + BIT_5);
//OR
byteWhoseBitsAreSetReset |= (BIT_3 | BIT_4 | BIT_5);
//To reset bit 3
byteWhoseBitsAreSetReset &= ~(BIT_3);
//To reset Multiple bits
byteWhoseBitsAreSetReset &= ~(BIT_3 + BIT_4 + BIT_5);
//OR
byteWhoseBitsAreSetReset &= ~(BIT_3 | BIT_4 | BIT_5);
I have this question, I got a dipswitch like a selector for different modes within my application. there are 3 bits to create the selector, but they are from different ports for example, the bit number 3 from portB, the bit number 1 from portC and the bit number 3 from portC, I want to move those 3 bits and storaged them in a register so that way I can create the selector, I already did it in assembler but i'm new into C programming, and till now the only similar answer was to move all the information from the entire port but anything related to a single bit. What's the command that I should use to move those bits from different ports?
In C, to get bit-level precision, you will need to use bitwise operators. For example, let's say you have an 8 bit container (which is also the smallest data type in C), but you're only interested in 1 bit; the best way to extract that bit would be to do a bitwise AND. In C, the bitwise AND is performed like this:
char c = 0x0B;
char bit = 0;
// Let's get the value of the second bit, so we must shift
// our bits to the right by one, then perform a bitwise AND
// to invalidate all other bits except the first.
bit = (c >> 1) & 0x01;
In binary, it would look like this:
00001011
Shift right once:
00000101
AND:
00000101 & 000000001
Yields: 000000001
To store different bits in the same memory location, you can use the bitwise-OR operator, which is the vertical pipe in C |. Here's an example:
char c = 0x00;
c = c | 0x01; /* c will now yield 0x01 */
c = c | 0xF0; /* c will now yield 0xF1 */
Final binary result: 11110001
With & and |, you can do some powerful things that are included in a lot of libraries to pass multiple options in one parameter. With 8 bits, you can store 8 different flags (or options), with 16 bits, 16 options. You could use this philosophy for your different ports. If you only have 4 bits worth of data in each port, you could use a 16-bit container, with 4 bits left that could simply be ignored. You'd do it like this:
short port_values = 0;
port_values = port_values | (port_a & 0x000F); // pretend port_a only has 4 bits of data and contains 0x0001
// port_values is now 0000 0000 0000 0001
// prepare to receive port_b by shifting the bits 4 to the left
port_values = port_values << 4;
// port_values is now 0000 0000 0001 0000
port_values = port_values | (port_b & 0x000F); // pretend port_b only has 4 bits of data and contains 0x000F
// port_values is now 0000 0000 0001 1111
// prepare to receive port_b by shifting the bits 4 to the left
port_values = port_values << 4;
// port_values is now 0000 0001 1111 0000
port_values = port_values | (port_c & 0x000F); // pretend port_c only has 4 bits of data and contains 0x0002
// port_values is now 0000 0001 1111 0010
I decide to do the following code, but when i'm running the the code it doesn't enter into the switch-case just retur after to set the last bit of "COMBINACION" what could be my error. i'm new is this world of C thanks for your time and consideration.
void SECUENCIA_1();
void SECUENCIA_2();
void SECUENCIA_3();
void SECUENCIA_4();
void SECUENCIA_5();
void SECUENCIA_6();
void SECUENCIA_ERROR();
void SALIDA_OK();
int i;
unsigned char COMBINACION;
void main(void) {
ADCON1 = 0x0F;
PORTA = 0x00;
TRISA = 0b00000;
PORTC = 0x00;
TRISC = 0b11100000;
PORTD = 0x00;
TRISD = 0b110000;
PORTE = 0x00;
TRISE = 0b000;
PORTB = 0x00;
TRISB = 0b11111111;
COMBINACION = 0x00;
COMBINACION = COMBINACION | (PORTCbits.RC5);
COMBINACION = COMBINACION << 1;
COMBINACION = COMBINACION | (PORTCbits.RC4);
COMBINACION = COMBINACION << 1;
COMBINACION = COMBINACION | (PORTDbits.RD3);
switch (COMBINACION)
{
case 0x0: SECUENCIA_ERROR; break;
case 0x1: SECUENCIA_1; break;
case 0x2: SECUENCIA_2; break;
case 0x3: SECUENCIA_3; break;
case 0x4: SECUENCIA_4; break;
case 0x5: SECUENCIA_5; break;
case 0x6: SECUENCIA_6; break;
default:SECUENCIA_ERROR;
}
}
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] &=
~((uint32_t)0x0F) <<
(0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] |=
(((uint32_t)EXTI_PortSourceGPIOx) <<
(0x04 * (EXTI_PinSourcex & (uint8_t)0x03)));
This is a piece of code from the STM32F4 board standard library. I understand every single operation but the entire code is really messy. Please accept the challenge and tell me what it is about in a more intuitive way.
And for simplicity, try to explain the situation when EXTI_PinSourcex is 0x01, and the EXTI_PortSourceGPIOx is 0x01 as well .
Any comments is appreciated, thanks in advance.
Ah, bitwise operator math.
It's easier to understand if you break it apart into pieces and "unoptimize" some of the language syntax. Let's also make the bigger variable convolutions easier to read:
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] &=
~((uint32_t)0x0F) <<
(0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
becomes:
#define cfgval_shift_2r (SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02])
cfgval_shift_2r = (cfgval_shift_2r) & ~((uint32_t)0x0F) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
Unraveling some of the constant bitwise math (such as ~((uint32_t)0x0F)):
cfgval_shift_2r = (cfgval_shift_2r) & 0xFFF0 << (0x04 * (EXTI_PinSourcex & 0x03));
Now we have something that's a little easier to read.
EXTI_PinSourcex == 0x00:
// cfgval_shift_2r = SYSCFG->EXTICR[0], because 0 shifted any number of bits is always 0
SYSCFG->EXTICR[0] = (SYSCFG->EXTICR[0]) & 0xFFF0 << (0x04 * (0 & 0x03));
// \ == 0 /
SYSCFG->EXTICR[0] = (SYSCFG->EXTICR[0]) & 0xFFF0;
So this takes the value of SYSCFG->EXTICR[0] and simply masks the least-significant byte off and assigns it back as the value.
EXTI_PinSourcex == 0x01:
// cfgval_shift_2r = SYSCFG->EXTICR[0], because (0x01 >> 0x02) == 0
SYSCFG->EXTICR[0] = (SYSCFG->EXTICR[0]) & 0xFFF0 << (0x04 * (0x01 & 0x03));
// \ == 0x04 * 0x01 == 0x04 /
SYSCFG->EXTICR[0] = (SYSCFG->EXTICR[0]) & 0xFFF0 << 0x04;
So this takes the value of SYSCFG->EXTICR[0], masks the least-significant byte off, shifts everything to the left by 4 bits, and assigns it back as the value.
You can apply a similar breakdown to the second operation.