I'm trying to read the linFrame sent from a slave LIN node, to identify when a particular bit has changed from from zero to one.
I'm sending a LIN message to a slave servo that commands it to move beyond a physical end-stop.
Once it physically hits the end-stop, its status message will set a single bit from zero to one. That bit identifies when the servo motor has stalled. My goal is to have the CAPL script detect that the motor has stalled.
My command message is sent by my CAPL script using the "output()" function. I'm unsure which function can read the response, but I think I have to send a header for the response message and then read the response frame.
variables
{
linFrame 0x03 ACT_CTRL_MT3 = {msgChannel=1}; // actuator control command
linFrame 0x21 ACT_STA_MT3 = {msgChannel=1}; // actuator status response
mstimer timer1;
}
on key 'c'
{
// command large angular position from servo at node 3
ACT_CTRL_MT3.RTR = 0;
ACT_CTRL_MT3.byte(0)=0x12; // section 4.5.9 of manual
ACT_CTRL_MT3.byte(1)=0xE4;
ACT_CTRL_MT3.byte(2)=0x14;
ACT_CTRL_MT3.byte(3)=0xFE; // max angle
ACT_CTRL_MT3.byte(4)=0xFF; // max angle
ACT_CTRL_MT3.byte(5)=0x00;
ACT_CTRL_MT3.byte(6)=0x00;
ACT_CTRL_MT3.byte(7)=0x40;
output(ACT_CTRL_MT3); // update command payload
// Send the frame header
ACT_CTRL_MT3.RTR = 1;
output(ACT_CTRL_MT3);
settimer(timer1,5000); // wait 5 seconds for motor to move
}
on timer timer1{
//send header of status message
ACT_STA_MT3.RTR = 1;
output(ACT_STA_MT3);
write("Reading message...");
//output message context
writelineex(1,1,"FrameId=%d Length=%d, 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X;", ACT_STA_MT3.ID, ACT_STA_MT3.DLC,
ACT_STA_MT3.byte(0), ACT_STA_MT3.byte(1), ACT_STA_MT3.byte(2), ACT_STA_MT3.byte(3), ACT_STA_MT3.byte(4), ACT_STA_MT3.byte(5), ACT_STA_MT3.byte(6), ACT_STA_MT3.byte(7));
}
The data that is written by the "writelinee" function is very different from the values seen in my LIN trace window in CANalyzer.
I find it especially strange that the ID field written out is different from the ID set in the variables section at the start of the code. In the code I define the frame ID of that status message as 0x21, but the write command gives a different value (0x35 I believe, although I'm away from my setup at the moment.
Unfortunately I do not have CANoe licence so I cannot check my code is functional. I have done similar thinkgs many times, so we should be good.
As I notices from your description you need to do there 3 things:
Send control LIN frame (ID 0x03) with some data to start the motor.
Send status LIN headers (ID 0x21) - remember that LIN Slave cannot initiate transmission on LIN bus so you need to provice LIN headers so LIN Slave can respond with some data.
Get response data from LIN header (data added by LIN slave to LIN header 0x21) - informs about motor stalling.
This is small modification to the code that you provided before. I also marked which things can be done in a different ways (you know CANoe have many features):
variables
{
linFrame 0x03 ACT_CTRL_MT3 = {msgChannel=1}; // actuator control command
linFrame 0x21 ACT_STA_MT3 = {msgChannel=1}; // actuator status response
mstimer timer1;
}
// starts the fun
on key 'c'
{
ACT_CTRL_MT3.RTR = 1; // RTR=1 means that you want to send entire message (header + data)
ACT_CTRL_MT3.byte(0)=0x12;
ACT_CTRL_MT3.byte(1)=0xE4;
ACT_CTRL_MT3.byte(2)=0x14;
ACT_CTRL_MT3.byte(3)=0xFE;
ACT_CTRL_MT3.byte(4)=0xFF;
ACT_CTRL_MT3.byte(5)=0x00;
ACT_CTRL_MT3.byte(6)=0x00;
ACT_CTRL_MT3.byte(7)=0x40;
output(ACT_CTRL_MT3); // this sends the message to the bus
settimer(timer1, 20); // set timer to trigger actions in 20 ms
}
// sending LIN headers (ID 0x21) cyclically
on timer timer1
{
// sends a single LIN header - you can also try to do it this way:
// ACT_STA_MT3.RTR=0;
// output(ACT_STA_MT3);
linTransmitHeader(ACT_STA_MT3); // or linTransmitHeader(0x21);
//set timer again to let LIN Slave respond if stalled later on
settimer(timer1, 20); // reset to execute every 20 ms
}
// event on receiving entire frame (header + response data) from LIN Slave
on linFrame ACT_STA_MT3
{
// not sure where is located your bit that is informing about stalling of the motor, but in this example I assumed it is on first position of byte 0
if (this.byte(0) & 0x01)
{
// you can put some other instructions here
write("Motor has stalled!");
}
// you can also log entire frame (I think your code was fine so I copied it):
writelineex(1,1,"FrameId=%d Length=%d, 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X;", this.ID, this.DLC, this.byte(0), this.byte(1), this.byte(2), this.byte(3), this.byte(4), this.byte(5), this.byte(6), this.byte(7));
}
PS. Try this and let me know if it compiles and works as expected as I literally wrote it in notepad. I will help you to fix potential errors.
About #Maciek answer, it actually works. Interessting to see that if you have your .ldf file in the "ldf explorer" by Vector with the correct signals assigned, then you could replace on linframe <frame> by on signal <any signal>.
The main difference is that linframe receives all the bytes, so you must use masks as #Maciek did here if (this.byte(0) & 0x01) which basically checks the first bit of the first byte. On the other hand, if you had a signal called e.g MyAlarmSignal which was defined in the .ldf file as the first bit of the frame, then you could simply read the signal using on signal MyAlarmSignal and inside the handler use $MyAlarmSignal or simply this to get the value.
Related
System is basic but I have terrible problem and I can not solve it pls help me. When my system works PIC keep running but clear the registers 4-5 times in a day.
How system should work:
-I have a PIC, pneumatic cylinder and 3 sensor(works with 24V DC).
-Main sensor take the signal from another system.
-When a signal came from main sensor, if the cyclinder is backward, cylinder should go to forward until forward sensor see it and if the cylinder is forward, cyclinder should come to backward until backward sensor see it.
Program:
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or
B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define goForward PIN_A0
#define comeBackward PIN_A1
#define main_sensor PIN_B0
#define positionSensorForward PIN_B5
#define positionSensorBackward PIN_B4
int1 pistonPositionedForward=0, pistonPositionedBackward=1;
int1 positionForwardReg=0, positionBackwardReg=0;
int1 pistonForwarding=0, pistonBackwarding=0;
#priority rb,ext
#int_RB NOCLEAR
void B_change()
{
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg&&pistonForwarding) //if forwarding and forward sensor see
{
disable_interrupts(INT_RB);
output_low(goForward);
pistonPositionedForward=1;
pistonPositionedBackward=0;
write_eeprom(0,1);
write_eeprom(1,0);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
else if(positionBackwardReg&&pistonBackwarding) //if backwarding and backward sensor see
{
disable_interrupts(INT_RB);
output_low(comeBackward);
pistonPositionedForward=0;
pistonPositionedBackward=1;
write_eeprom(0,0);
write_eeprom(1,1);
pistonForwarding=0;
pistonBackwarding=0;
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
}
clear_interrupt(int_rb);
}
#int_ext NOCLEAR
void ext_interrupt()
{
disable_interrupts(INT_EXT);
positionForwardReg=input(positionSensorForward);
positionBackwardReg=input(positionSensorBackward);
if(positionForwardReg^positionBackwardReg) //if one of position sensor is see then position according to sensor, else position according to memory
{
pistonPositionedForward=positionForwardReg;
pistonPositionedBackward=positionBackwardReg;
}
if(pistonPositionedForward)
{
pistonBackwarding=1;
pistonForwarding=0;
output_high(comeBackward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
else if(pistonPositionedBackward)
{
pistonForwarding=1;
pistonBackwarding=0;
output_high(goForward);
clear_interrupt(int_rb);
enable_interrupts(INT_RB);
}
clear_interrupt(int_ext);
}
void main()
{
//to remember last position after power off
pistonPositionedForward=read_eeprom(0);
pistonPositionedBackward==read_eeprom(1);
set_tris_a(0x00);
set_tris_b(0xFF);
output_a(0x00);
delay_ms(1000);
ext_int_edge(L_TO_H);
clear_interrupt(int_ext);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
while(TRUE)
{
}
}
And my circuit:
CIRCUIT
*j2,j3 connected selonoid valve
*J4,J5,J6 connected 3 sensors 1. pin +24VDC,2. pin GND, 3.pin sensor data
***B1 and B2 connections changed. Now B1 connected to B5,B2 connected to B4
And These are I tried:
-I have 3 PIC all of them do same thing
-I changed 24V power supply
-I cancelled 7805 and 7812 and I connected seperate 5V power supply istead of 7805.
I am debugging via LEDs. Sometimes system stop running just waiting at one of positions. Take main sensor signal but doesnot anything, And pistonPositionedForward and pistonPositionedBackward register values are 0. I cant find problem how can it clear these registers?
You have unconnected pins on RB that are configured as inputs, with no internal pull ups set. Electrical noise may well trigger unwanted interrupts on PORTB, that has been known to happen.
The use of interrupts is making the overall logic a bit hard to follow for such a simple device. Have you tried rewriting the program NOT using interrupts (except maybe for EXT)? It should not take long and I think it may greatly improve the reliability - and maintainability, without impacting performance of the physical system.
I suggest you first configure the unused PORTA and PORTB pins as outputs, and see if the problem goes away. If that fails, a rewrite not using interrupts should take no more than an hour. This would probably make sense since that is probably way shorter than the time you have already spent chasing the issue.
Reading the description, I came up with this solution.
#include <16F628A.h>
#FUSES NOWDT //No Watch Dog Timer
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage prgming, B5(PIC18) used for I/O
#use delay(crystal=4000000)
#use fast_io(a)
#use fast_io(b)
#define FWD_MOVE PIN_A0
#define BACK_MOVE PIN_A1
#define PORTA_RESET (0x00) // outputs=LO, unused pins as outputs
#define PORTA_TRISTATE (0x00)
#define EXT_SENSOR PIN_B0
#define FWD_REST PIN_B5
#define BACK_REST PIN_B4
#define PORTB_RESET (0xCE) // can't use pull ups because of HW logic...
#define PORTB_TRISTATE (0x31)
#define EEPROM_STATUS_ADDR (0x0000)
#define EEPROM_STATUS_FWD (0x01)
#define EEPROM_STATUS_BACK (0x00)
int1 extLast;
int1 extCur;
void main()
{
// setup
output_a(PORTA_RESET):
output_b(PORTB_RESET):
// setting to last known state...
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// This will activate the outputs to make sure we have good
// positioning.
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // EEPROM error... I'll let you decide what to do here.
// either move forward or back.
// this implementation goes back by default.
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
// falling through...
case EEPROM_STATUS_BACK:
output_high(BACK_MOVE);
break;
case EEPROM_STATUS_FWD:
output_high(FWD_MOVE);
break;
}
// activate outputs... watch your fingers!
set_tris_a(PORTA_TRISTATE);
set_tris_b(PORTB_TRISTATE);
extLast = input(EXT_SENSOR);
for (;;)
{
// read external sensor, take action.
extCur = input(EXT_SENSOR);
if (extCur && !extlast)
{
// safety check.
output_low(FWD_MOVE);
output_low(BACK_MOVE);
// control logic
switch(eeprom_read(EEPROM_STATUS_ADDR))
{
default: // should never happen.
// falling through...
case EEPROM_STATUS_BACK:
output_high(FWD_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_FWD);
disable_interrupts(GLOBAL);
break;
case EEPROM_STATUS_FWD:
output_high(BACK_MOVE);
eeprom_write(EEPROM_STATUS_ADDR, EEPROM_STATUS_BACK);
disable_interrupts(GLOBAL);
break;
}
}
extLast = extCur;
// mechanical interface:
// read the limit guards and stop movement when done.
if (input(FWD_REST))
{
output_low(FWD_MOVE);
}
if (input(BACK_REST))
{
output_low(BACK_MOVE);
}
}
}
Of course, I could not check the above code on-site :).
After reviewing the schematics, I must also advise to add 1N4001 diodes in parallel of the 2 output MOSFETS to give them better protection against reverse voltage spikes. MOSFET built-in diodes are not very sturdy. 1N4148 or 1N914 would work there as well.
The 16F628 has very limited stack space. It looks like you are experiencing stack overflow during the call to write_eeprom. Calling write_eeprom from an interrupt may not be such a good idea after all.
There was a bug in older CCS compilers, related to the use of write_eeprom. It seems write_eeprom is enabling interrupts during the call. I've added calls to disable interrupts after the writes. I don't know if that bug was fixed, since I never use CCS.
[EDIT] after checking your HW. I realized you cannot use internal pull-ups because the HW logic is positive-going. The pull-ups in the PIC are meant to work with NPN transistors in the open collector configuration (emitter to ground). I changed the code accordingly.
The way you write to EEPROM is not good. eeprom writes take time, and the second write is usually taken care of in the eeprom interrupt. The CCS bug that enables the global interrupt and unmask the EEIE in eeprom_write does not help. Unhandled interrupts do generate a reset.
I wrote a command line c tool generating an sine wave and playing it using CoreAudio on the default audio output. I am initializing a
AURenderCallbackStruct and initialize an AudioUnit using AudioUnitInitialize (as already discussed in this forum). All this is working as intended, but when it comes to closing the program I am not able to close the AudioUnit, neither with using AudioOutputUnitStop(player.outputUnit); nor AudioOutputUnitStop(player.outputUnit); nor
AudioComponentInstanceDispose(player.outputUnit);
The order of appearance of these calls in the code does not change the behavior.
The program is compiled without error messages, but the sine is still audible as long as the rest of the program is running.
Here is the code I'm using for initializing the AudioUnit:
void CreateAndConnectOutputUnit (ToneGenerator *player) {
AudioComponentDescription outputcd = {0};
outputcd.componentType = kAudioUnitType_Output;
outputcd.componentSubType = kAudioUnitSubType_DefaultOutput;
outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent comp = AudioComponentFindNext (NULL, &outputcd);
if (comp == NULL) {
printf ("can't get output unit");
exit (-1);
}
AudioComponentInstanceNew(comp, &player->outputUnit);
// register render callback
AURenderCallbackStruct input;
input.inputProc = SineWaveRenderCallback;
input.inputProcRefCon = player;
AudioUnitSetProperty(player->outputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Output,
0,
&input,
sizeof(input);
// initialize unit
AudioUnitInitialize(player->outputUnit);
}
In my main program I'm starting the AudioUnit and the sine wave.
void main {
// code for doing various things
ToneGenerator player = {0}; // create a sound object
CreateAndConnectOutputUnit (&player);
AudioOutputUnitStart(player.outputUnit);
// waiting to listen to the sine wave
sleep(3);
// attempt to stop the sound output
AudioComponentInstanceDispose(player.outputUnit);
AudioUnitUninitialize(player.outputUnit);
AudioOutputUnitStop(player.outputUnit);
//additional code that should be executed without sine wave being audible
}
As I'm new to both, this forum as well as programming in Xcode I hope that I could explain this issue in a way that you can help me out and I hope that I didn't miss the answer somewhere in the forum while searching for a solution.
Thank you in advance for your time and input,
Stefan
You should manage and unmanage your audio unit in a logical order. It doesn't make sense to stop playback on an already uninitialized audio unit, which had in fact previously been disposed of in the middle of the playback. Rather than that, try the following order:
AudioOutputUnitStop(player.outputUnit); //first stops playback
AudioUnitUninitialize(player.outputUnit); //then deallocates unit's resources
AudioComponentInstanceDispose(player.outputUnit); //finally disposes of the AU itself
The sine wave command line app you're after is a well elaborated lesson in this textbook. Please read it step by step.
Last, but not least, your question has nothing to do with C++, CoreAudio is a plain-C API, so C++ in both your title and tag are wrong and misleading.
An Audio Unit runs in an asynchronous thread that may not actually stop immediately when you call AudioOutputUnitStop. Thus, it may work better to wait a fraction of a second (at least a couple audio callback buffer durations in time) before calling AudioUnitUninitialize and AudioComponentInstanceDispose on a potentially still running audio unit.
Also, check to make sure your player.outputUnit value is a valid unit (and not an uninitialized or trashed variable) at the time you stop the unit.
I have a web application that submits an ajax request (to start an IR LED sending data) onmousedown, and stops it onmouseup. Generally works.
However if one is clicking fast I get the stop command going to the LED control before the start command. I added code to track order and the actual event submission (open on XMLHttpRequest) is in the proper order (and they are async). Then I instrumented the onstatechange to log each state and a time stamp when that state change occurs, and what I see is occasionally while the started (0) and server connection initialized (1) states occur in the proper order, the "received" (2) and subsequent states occur out of order.
My first question is whether these events are SUPPOSED to occur in the order submitted?
And if so, any idea what could cause them not to?
And if processing order is not assured, is there any way to force it (short of building my own queue and allowing only one ajax call to be outstanding, which is a paint)?
To demonstrate the issue (with client on Windows 8.1 and Chrome 39.0.2171.71m as well as IE 11.0.14, server on a raspberry pi running php 5.4.4-14+deb7u11 and Apache/2.2.22), here's a log captured from a series of events. It's small and a bit obscure, so I tried to color code each click (green, blue, orange in sequence), and the state code is in the 3rd column. Notice up (highlighted in yellow) precedes the down in the 3rd click once it gets to state 2.
Next is the code that submits it. I believe a lot of it to be irrelevant to the problem but am including it just in case. Note "RemoteFunc" is called both on mouseup and mousedown as well as (for separate objects) plain click, but each with a different func, then a POST is done. Each object either uses mouseup/mousedown or onclick, never both.
The processing time for the post can easily be greater then the duration of a mouse click, so it is not unexpected for a new "down" to come before the first "down" or "up" completes, but the issue is that I want them to process in order. And indeed they seem to be submitting in order and starting (going to state 1) in order, but are received out of order, so I assume ajax is creating multiple connections, and the response to those connections is getting out of order.
Which makes me suspect that order is not guaranteed. But I haven't found anything to confirm that (other than absence of a guarantee to be in order).
Any easy solution other than to manually build a queue and single stream the requests?
function RemoteFunc(str,func)
{
var xmlhttp=new XMLHttpRequest();
if(event.button!=0)
{
AddMessageEntry(str + " dismissed - not left click",true);
return true; //Only respond to left mouse down so context menus do not fire click and cause a competing event
}
if(taskrunning!="")
{
AddMessageEntry(str + " ignored, " + taskrunning + " already running",false);
return true;
}
if(func=="remoteup.cgi") { updown=" mouse-up"; }
else if (func=="remotedown.cgi") { updown=" mouse-down"; }
else { updown=""; }
AddMessageEntry(str + updown + " Started",true);
xmlhttp.onreadystatechange=function()
{
// if (xmlhttp.readyState==4)
// {
if(func=="remoteup.cgi") { updown=" mouse-up"; } // because this can run in overlapping functions we need to recalculate this here
else if (func="remotedown.cgi") { updown=" mouse-down"; }
else { updown=""; }
AddMessageEntry(str + updown + " " + states[xmlhttp.readyState] + " " + xmlhttp.responseText,false);
if(xmlhttp.readyState==4) {taskrunning="";}
// }
}
if(event.type.toString()=="click") // We only need to track running tasks invoked with a click -- mousedown/up are async since they run continually (from down) until stopped (from up)
{
taskrunning=str;
}
xmlhttp.open("POST","cgi-bin/" + func + "?Button="+str,true);
xmlhttp.send();
}
I am writing a program which writes to a wireless device on the serial port. The firmware has a feature which will confirm the last message sent to the device was ok, so I'm trying to make use of that and update the settings readout on the GUI rather than ask the device for all of its settings every time I make a change. I think my code will explain it a little better:
// global variable
bool queryStatusOK;
//event handler response from serial port
this.QueryStatusReponseEvent += QueryStatusResponse;
private void QueryStatusResponse(byte[] packet)
{
if (packet[3] == 0) queryStatusOK = true;
else queryStatusOK = false;
}
public void setParameter(string device)
{
//send command to serial port to change a single device parameter
Thread.Sleep(100); //sleep thread so 2 commands are not sent at once
//send command to confirm previous command was received
Thread.Sleep(100); //sleep thread to give time for confirmation to receive
if (queryStatusOK)
{
//update GUI at this point
}
}
The program is not consistent. It works sometimes, but not always. Even if I extend the thread sleep to a full second to give the boolean time to update, it still sometimes will not hit it. Can anyone suggest a better way to do this?
Thanks!
Mike
I am developing an application in Atmel Studio 6 using the xMega32a4u. I'm using the TWI libraries provided by Atmel. Everything is going well for the most part.
Here is my issue: In order to update an OLED display I am using (SSD1306 controller, 128x32), the entire contents of the display RAM must be written immediately following the I2C START command, slave address, and control byte so the display knows to enter the data into the display RAM. If the control byte does not immediately precede the display RAM package, nothing works.
I am using a Saleae logic analyzer to verify that the bus is doing what it should.
Here is the function I am using to write the display:
void OLED_buffer(){ // Used to write contents of display buffer to OLED
uint8_t data_array[513];
data_array[0] = SSD1306_DATA_BYTE;
for (int i=0;i<512;++i){
data_array[i+1] = buffer[i];
}
OLED_command(SSD1306_SETLOWCOLUMN | 0x00);
OLED_command(SSD1306_SETHIGHCOLUMN | 0x00);
OLED_command(SSD1306_SETSTARTLINE | 0x00);
twi_package_t buffer_send = {
.chip = OLED_BUS_ADDRESS,
.buffer = data_array,
.length = 513
};
twi_master_write(&TWIC, &buffer_send);
}
Clearly, this is very inefficient as each call to this function recreates the entire array "buffer" into a new array "data_array," one element at a time. The point of this is to insert the control byte (SSD1306_DATA_BYTE = 0x40) into the array so that the entire "package" is sent at once, and the control byte is in the right place. I could make the original "buffer" array one element larger and add the control byte as the first element, to skip this process but that makes the size 513 rather than 512, and might mess with some of the text/graphical functions that manipulate this array and depend on it being the correct size.
Now, I thought I could write the code like this:
void OLED_buffer(){ // Used to write contents of display buffer to OLED
uint8_t data_byte = SSD1306_DATA_BYTE;
OLED_command(SSD1306_SETLOWCOLUMN | 0x00);
OLED_command(SSD1306_SETHIGHCOLUMN | 0x00);
OLED_command(SSD1306_SETSTARTLINE | 0x00);
twi_package_t data_control_byte = {
.chip = OLED_BUS_ADDRESS,
.buffer = data_byte,
.length = 1
};
twi_master_write(&TWIC, &data_control_byte);
twi_package_t buffer_send = {
.chip = OLED_BUS_ADDRESS,
.buffer = buffer,
.length = 512
};
twi_master_write(&TWIC, &buffer_send);
}
/*
That doesn't work. The first "twi_master_write" command sends a START, address, control, STOP. Then the next such command sends a START, address, data buffer, STOP. Because the control byte is missing from the latter transaction, this does not work. All I need is to insert a 0x40 byte between the address byte and the buffer array when it is sent over the I2C bus. twi_master_write is a function that is provided in the Atmel TWI libraries. I've tried to examine the libraries to figure out its inner workings, but I can't make sense of it.
Surely, instead of figuring out how to recreate a twi_write function to work the way I need, there is an easier way to add this preceding control byte? Ideally one that is not so wasteful of clock cycles as my first code example? Realistically the display still updates very fast, more than enough for my needs, but that does not change the fact this is inefficient code.
I appreciate any advice you all may have. Thanks in advance!
How about having buffer and data_array pointing to the same uint8_t[513] array, but with buffer starting at its second element. Then you can continue to use buffer as you do today, but also use data_array directly without first having to copy all the elements from buffer.
uint8_t data_array[513];
uint8_t *buffer = &data_array[1];