ATTINY84でSPIを受けてみます。Arduino化せずに、Atmel Studioを使う。例によってデータシートを読むのが面倒なのでChatGPTに聞いてみる。
ちなみに、Timer1高速PWMについてChatGPTに聞いてみるの回で
キミ時々間違うよね、、、って言ったら、
マイコンやFPGAもめっちゃ学習しているけど、エキスパートじゃないので専門家にはかなわないところがあるって言ってた。ってことは専門家がChatGPTを使っちゃいかんってことじゃんね。技術の発展には寄与しないって自分で言ってるってことやね。
と言いながら、わたくしのような下賤のものは専門家じゃないのでChatGPTでやる。
SundayEngineer :
please generrate an example code of spi slave on attiny84 using usi peripheral.
CharGPT :
Certainly! Here's an example code for SPI slave communication on an ATtiny84 microcontroller using the USI (Universal Serial Interface) peripheral. This code demonstrates how to receive data from a SPI master.
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PB2 #define SPI_MOSI_PIN PB1 #define SPI_MISO_PIN PB0 #define SPI_SCK_PIN PB4 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRB &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } int main(void) { // Set the SS, MOSI, and SCK pins as inputs with pull-up resistors DDRB &= ~((1 << SPI_SS_PIN) | (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN)); PORTB |= (1 << SPI_SS_PIN) | (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN); // Initialize SPI slave SPI_SlaveInit(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { // Process received data // ... // Reset the received data flag spiDataReceived = 0; } } return 0; }
In this code, the SPI slave is set up to receive data from the SPI master. The SPI_SlaveInit() function initializes the necessary registers and enables the USI as a SPI slave. The ISR(USI_OVF_vect) interrupt service routine is triggered when a byte of data is received. The received data is stored in the spiDataReceived variable, which can be processed in the main loop.
Please note that you need to adjust the pin definitions (SPI_SS_PIN, SPI_MOSI_PIN, SPI_MISO_PIN, SPI_SCK_PIN) according to your specific hardware setup. Make sure to connect the corresponding pins on the ATtiny84 to the appropriate pins on the SPI master device.
Also, don't forget to set up the SPI master device to communicate with the ATtiny84 SPI slave using the correct clock rate, data order, and SPI mode.
ごたく並べるヒマあったらpinoutくらい確認してからやれやってちょっとイラっとくる。
SundayEngineer :
please correct the ATTINY84 spi pins. MOSI is PA6. MISO is PA5. SCK is PA4. these are written in the ATTINY84 datasheet.
CharGPT :
Apologies for the incorrect pin mappings. Here's the corrected code for SPI slave communication on ATtiny84 with the proper pin assignments for MOSI, MISO, and SCK based on the ATtiny84 datasheet:
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PB2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } int main(void) { // Set the SS, MOSI, and SCK pins as inputs with pull-up resistors DDRB &= ~(1 << SPI_SS_PIN); DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN)); PORTB |= (1 << SPI_SS_PIN); PORTA |= (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN); // Initialize SPI slave SPI_SlaveInit(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { // Process received data // ... // Reset the received data flag spiDataReceived = 0; } } return 0; }
Please note that in this code, I have made the necessary changes to use the correct pin assignments for MOSI, MISO, and SCK on the ATtiny84 microcontroller, based on the information from the datasheet. Ensure that you connect the corresponding pins on the ATtiny84 to the appropriate pins on the SPI master device.
謝罪はあっても教えてもらったことに感謝しない態度にちょっとムッとする。まぁAIのふり見て我が振りなおせっと思ってこらえる。
SSピンがちょっと都合が悪いので変えてもらおう。
SundayEngineer :
please change the ss pin to PA2.
CharGPT :
Apologies for the oversight. Here's the corrected code with the SS pin changed to PA2:
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PA2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } int main(void) { // Set the SS, MOSI, and SCK pins as inputs with pull-up resistors DDRA &= ~((1 << SPI_SS_PIN) | (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN)); PORTA |= (1 << SPI_SS_PIN) | (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN); // Initialize SPI slave SPI_SlaveInit(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { // Process received data // ... // Reset the received data flag spiDataReceived = 0; } } return 0; }
In this code, the SS pin is now assigned to PA2, as requested. Make sure to adjust your hardware connections accordingly.
"Apologies for the oversight."って今回、怒ってないので謝られても困る。が、わたくしのような下劣な人間は必要以上に卑屈な態度をとって相手の態度が委縮することを狙ったりするので、こうやって人を困らせてたんだと思うとちょっと心が痛む。
受信データによってちょっと動きを付けたいので追加をお願いする。
SundayEngineer :
please add a function when 0x01 is received set PA1 output high if not set PA1 low.
CharGPT :
Certainly! Here's the modified code that sets PA1 output high when receiving 0x01 and low for any other received value:
#include <avr/interrupt.h> #define SPI_SS_PIN PA2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 #define OUTPUT_PIN PA1 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } void setOutputPinState(uint8_t data) { if (data == 0x01) { PORTA |= (1 << OUTPUT_PIN); // Set PA1 output high } else { PORTA &= ~(1 << OUTPUT_PIN); // Set PA1 output low } } int main(void) { // Set the SS, MOSI, SCK, and output pins as inputs with pull-up resistors DDRA &= ~((1 << SPI_SS_PIN) | (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN)); PORTA |= (1 << SPI_SS_PIN) | (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN); // Initialize SPI slave SPI_SlaveInit(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { setOutputPinState(spiDataReceived); // Set output pin based on received data // Reset the received data flag spiDataReceived = 0; } } return 0; }
I added a new function setOutputPinState() that checks if the received data is equal to 0x01. If it is, it sets the PA1 output pin high; otherwise, it sets the PA1 output pin low. You can replace the comment // Process received data in the main loop with your desired code to handle the received data.
Please make sure to adjust your hardware connections accordingly.
よーし、なんとなくそれらしいので試してみる。
ATTINY85でSPI Masterのコードをいじって、0x01と0xDAを交互に出すようにする。mainをこうする。
int main(void) { USI_SPI_MasterInit(); while (1) { sei(); PORTB&=~PIN_SS; USI_SPI_MasterTransmit(0x01); while(txCompleted==0){ // } PORTB|=PIN_SS; cli(); _delay_us(100); sei(); PORTB&=~PIN_SS; USI_SPI_MasterTransmit(0xDA); while(txCompleted==0){ // } PORTB|=PIN_SS; cli(); _delay_ms(1); } }
で、ChatGPTの出したコードをATTINY84に書き込んで、ATTINY85とつないで、
うーん、、、なぜか8回中1回しか成功していない。ChatGPTのせいなのか、なんなのか、、、
よく見ると出すほうが1clock多い。すまソorz
で、これは送信側のスピードを落とす
ocr0a_value=0x28;// 0x20 previously
ことで解決。
で今度は、OUTPUT_PIN(ロジアナのA4)が微動だにしなくなった。で、USI_OVF_vectでポート上げ下げしてみると、、、
ずれとる。そんで、送信側クロックが多いと、周期的に成功しているように見えてたんか。ってことは、CSでUSIをリセットせんといかんってことやな。で、データシートを見ると、SSが割り当てられているPA2はPCINT(ピン変化割り込み)しかできない。で、割り込み時のレベルで判断する方法もあるけど、処理時間がもったいない。立下りエッジで割り込み発生させるにはINT0機能のPB2にするしかない。ってことは最初にChatGPTが提案してきたやつに戻すってことになる。いやーまずいなー(´・ω・`; )朝令暮改。嫌われるやつやー。が、しょがない。
SundayEngineer :
i am very sorry but i would like you to change the ss pin to PB2. and then, please implement INT0 falling edge interrupt and an instruction to clear USI counter in the interrupt handler.
CharGPT :
No problem! Here's the modified code with the SS pin changed to PB2 and the implementation of INT0 falling edge interrupt, including an instruction to clear the USI counter in the interrupt handler:
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PB2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 #define OUTPUT_PIN PA1 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } void setOutputPinState(uint8_t data) { if (data == 0x01) { PORTA |= (1 << OUTPUT_PIN); // Set PA1 output high } else { PORTA &= ~(1 << OUTPUT_PIN); // Set PA1 output low } } void INT0_Init(void) { // Set INT0 (PB2) as input with pull-up resistor DDRB &= ~(1 << PB2); PORTB |= (1 << PB2); // Set the falling edge of INT0 as the trigger EICRA |= (1 << ISC01); // Enable external interrupt 0 EIMSK |= (1 << INT0); // Enable global interrupts sei(); } ISR(INT0_vect) { USISR |= (1 << USICNT0); // Clear USI counter } int main(void) { // Set the SS, MOSI, SCK, and output pins as inputs with pull-up resistors DDRB &= ~(1 << SPI_SS_PIN); DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN)); PORTB |= (1 << SPI_SS_PIN); PORTA |= (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN); // Initialize SPI slave SPI_SlaveInit(); // Initialize INT0 falling edge interrupt INT0_Init(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { setOutputPinState(spiDataReceived); // Set output pin based on received data // Reset the received data flag spiDataReceived = 0; } } return 0; }
In this updated code, the SS pin is now assigned to PB2 as requested. The INT0_Init() function initializes the INT0 falling edge interrupt by setting the corresponding interrupt trigger and enabling the external interrupt. The ISR(INT0_vect) interrupt service routine is triggered on the falling edge of INT0 and clears the USI counter by setting the USICNT0 bit in USISR.
Please make sure to adjust your hardware connections accordingly.
えーっと、ATTINY84に存在しないレジスタ名があります。くぅー反撃するんかーい。朝令暮改でイラっとする気持ちはわかりますうう。
SundayEngineer :
To enable INT0 falling edge interrupt MCUCR=((MCUCR&0xFC)|0x02) and GIMSK|=0x40 are necessary. EICRA and EIMSK registers do not exist in ATTINY84.
CharGPT :
Apologies for the confusion. You are correct that the ATtiny84 does not have the EICRA and EIMSK registers for configuring the external interrupts. Instead, it uses the MCUCR and GIMSK registers. Here's the updated code with the correct configuration for the INT0 falling edge interrupt:
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PB2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 #define OUTPUT_PIN PA1 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } void setOutputPinState(uint8_t data) { if (data == 0x01) { PORTA |= (1 << OUTPUT_PIN); // Set PA1 output high } else { PORTA &= ~(1 << OUTPUT_PIN); // Set PA1 output low } } void INT0_Init(void) { // Set INT0 (PB2) as input with pull-up resistor DDRB &= ~(1 << PB2); PORTB |= (1 << PB2); // Set the falling edge of INT0 as the trigger MCUCR = (MCUCR & 0xFC) | (1 << ISC01); // Enable external interrupt 0 GIMSK |= (1 << INT0); // Enable global interrupts sei(); } ISR(INT0_vect) { USISR |= (1 << USICNT0); // Clear USI counter } int main(void) { // Set the SS, MOSI, SCK, and output pins as inputs with pull-up resistors DDRB &= ~(1 << SPI_SS_PIN); DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN)); PORTB |= (1 << SPI_SS_PIN); PORTA |= (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN); // Initialize SPI slave SPI_SlaveInit(); // Initialize INT0 falling edge interrupt INT0_Init(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { setOutputPinState(spiDataReceived); // Set output pin based on received data // Reset the received data flag spiDataReceived = 0; } } return 0; }
I apologize for the oversight in the previous response. This updated code correctly uses the MCUCR register to configure the INT0 falling edge trigger and the GIMSK register to enable the external interrupt.
Please make sure to adjust your hardware connections accordingly.
さっき合わせて言えばよかったけど、これ
USISR |= (1 << USICNT0); // Clear USI counter
じゃUSIカウンタはクリアになってないやん。
USISR&=0xF0;
が正しいような。
SundayEngineer :
"USISR |= (1 << USICNT0);" can not clear USI counter. i believe "USISR&=0xF0;" can clear USI counter. could you please confirm which instruction is correct.
CharGPT :
Apologies for the confusion. You are correct, and I apologize for the incorrect instruction. The correct instruction to clear the USI counter is USISR &= 0xF0;. Thank you for pointing out the mistake.
Here's the updated code with the corrected instruction to clear the USI counter in the INT0 interrupt handler:
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PB2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 #define OUTPUT_PIN PA1 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } void setOutputPinState(uint8_t data) { if (data == 0x01) { PORTA |= (1 << OUTPUT_PIN); // Set PA1 output high } else { PORTA &= ~(1 << OUTPUT_PIN); // Set PA1 output low } } void INT0_Init(void) { // Set INT0 (PB2) as input with pull-up resistor DDRB &= ~(1 << PB2); PORTB |= (1 << PB2); // Set the falling edge of INT0 as the trigger MCUCR |= (1 << ISC01); // Enable external interrupt 0 GIMSK |= (1 << INT0); // Enable global interrupts sei(); } ISR(INT0_vect) { USISR &= 0xF0; // Clear USI counter } int main(void) { // Set the SS, MOSI, SCK, and output pins as inputs with pull-up resistors DDRB &= ~(1 << SPI_SS_PIN); DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN)); PORTB |= (1 << SPI_SS_PIN); PORTA |= (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN); // Initialize SPI slave SPI_SlaveInit(); // Initialize INT0 falling edge interrupt INT0_Init(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { setOutputPinState(spiDataReceived); // Set output pin based on received data // Reset the received data flag spiDataReceived = 0; } } return 0; }
Thank you for catching the error, and I apologize for any confusion caused. The updated code now correctly clears the USI counter using USISR &= 0xF0; in the INT0 interrupt handler.
Please ensure that you adjust your hardware connections accordingly.
(´ Д`;)どうやー
やっと期待通りになったでー
SundayEngineer :
thank u for the updated code. your code works as expected.
CharGPT :
You're welcome! I'm glad to hear that the updated code works as expected. If you have any more questions or need further assistance, feel free to ask. Happy coding!
いやー意外と難しいこともできるやんって言いたいけど、結構、こっちから答えを教えちゃってるよなー。こいつ一人で作り上げるというより、協力して修正しながらじゃないと正解にはたどり着けないと感じる。こいつぜってーデータシート読んでないよなーとも思うし。データシートを読んで、かみ砕いて、理解したことを記憶しておいて、コーディングに活かしてほしいところです。、、、人間じゃないとやれんかー。結局こいつってweb上のデータからそれらしいのをもってきているだけだもんなー。正確さが要求される分野には向いてないよゼッタイ。ただ、英語で指示する練習になる(。´-д-)
追記)よくよくみるとOUTPUT_PINがINPUTになっているので、PU<-->HZって動きになってる。
DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN));
が間違いで、
DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN));
DDRA |= (1 << OUTPUT_PIN);
ってせんといかん。
SundayEngineer :
i am sorry but i found a mistake on your latest code. the DDR setting for OUTPUT_PIN is not set as output. please correct this point.
CharGPT :
Apologies for the oversight. You're right, the DDR setting for the OUTPUT_PIN was missing. Here's the corrected code with the DDR setting for OUTPUT_PIN as an output:
#include <avr/io.h> #include <avr/interrupt.h> #define SPI_SS_PIN PB2 #define SPI_MOSI_PIN PA6 #define SPI_MISO_PIN PA5 #define SPI_SCK_PIN PA4 #define OUTPUT_PIN PA1 volatile uint8_t spiDataReceived = 0; void SPI_SlaveInit(void) { // Set the MISO pin as an input DDRA &= ~(1 << SPI_MISO_PIN); // Enable SPI, enable SPI interrupt, and set the device as a slave USICR = (1 << USIWM0) | (1 << USICS1) | (1 << USIOIE); // Enable global interrupts sei(); } ISR(USI_OVF_vect) { // Read received data from the USIDR register spiDataReceived = USIDR; // Clear the overflow interrupt flag USISR |= (1 << USIOIF); } void setOutputPinState(uint8_t data) { if (data == 0x01) { PORTA |= (1 << OUTPUT_PIN); // Set PA1 output high } else { PORTA &= ~(1 << OUTPUT_PIN); // Set PA1 output low } } void INT0_Init(void) { // Set INT0 (PB2) as input with pull-up resistor DDRB &= ~(1 << PB2); PORTB |= (1 << PB2); // Set the falling edge of INT0 as the trigger MCUCR |= (1 << ISC01); // Enable external interrupt 0 GIMSK |= (1 << INT0); // Enable global interrupts sei(); } ISR(INT0_vect) { USISR &= 0xF0; // Clear USI counter } int main(void) { // Set the SS, MOSI, SCK, and output pins as inputs with pull-up resistors DDRB &= ~(1 << SPI_SS_PIN); DDRA &= ~((1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN)); DDRA |= (1 << OUTPUT_PIN); // Set OUTPUT_PIN as an output PORTB |= (1 << SPI_SS_PIN); PORTA |= (1 << SPI_MOSI_PIN) | (1 << SPI_SCK_PIN) | (1 << OUTPUT_PIN); // Initialize SPI slave SPI_SlaveInit(); // Initialize INT0 falling edge interrupt INT0_Init(); // Main loop while (1) { // Do something with the received SPI data if (spiDataReceived != 0) { setOutputPinState(spiDataReceived); // Set output pin based on received data // Reset the received data flag spiDataReceived = 0; } } return 0; }
I apologize for any inconvenience caused by the missing DDR setting. The code has been updated to set OUTPUT_PIN as an output by adding DDRA |= (1 << OUTPUT_PIN); in the appropriate section.
Thank you for bringing this to my attention, and please let me know if you have any further questions!
予想通りに修正してきた。
SundayEngineer :
thx. it works correctly.
CharGPT :
You're welcome! I'm glad to hear that the code is now working correctly. If you have any more questions or need further assistance, feel free to ask. Happy coding!
コメントをお書きください