У меня есть буфер, который я использую для UART, который объявлен так:
union Eusart_Buff {
uint8_t b8[16];
uint16_t b9[16];
};
struct Eusart_Msg {
uint8_t msg_posn;
uint8_t msg_len;
union Eusart_Buff buff;
};
struct Eusart {
struct Eusart_Msg tx;
struct Eusart_Msg rx;
};
extern volatile struct Eusart eusart;
А вот функция, которая заполняет буфер (который будет отправлен с использованием прерываний):
void eusart_msg_transmit (uint8_t n, void *msg)
{
if (!n)
return;
/*
* The end of the previous transmission will reset
* eusart.tx.msg_len (i.e. ISR is off)
*/
while (eusart.tx.msg_len)
;
if (data_9b) {
memcpy((void *)eusart.tx.buff.b9, msg,
sizeof(eusart.tx.buff.b9[0]) * n);
} else {
memcpy((void *)eusart.tx.buff.b8, msg,
sizeof(eusart.tx.buff.b8[0]) * n);
}
eusart.tx.msg_len = n;
eusart.tx.msg_posn = 0;
reg_PIE1_TXIE_write(true);
}
В момент использования memcpy()
я знаю, что никто другой не собирается использовать буфер (атомарный), потому что цикл while
гарантирует, что последнее сообщение было отправлено, и, следовательно, прерывание отключено..
Безопасно ли отбрасывать volatile
таким образом, чтобы я мог использовать memcpy()
или я должен сделать функцию, которая может быть названа memcpy_v()
, подобной этой, чтобы она была безопасной?:
void *memcpy_vin(void *dest, const volatile void *src, size_t n)
{
const volatile char *src_c = (const volatile char *)src;
char *dest_c = (char *)dest;
for (size_t i = 0; i < n; i++)
dest_c[i] = src_c[i];
return dest;
}
volatile void *memcpy_vout(volatile void *dest, const void *src, size_t n)
{
const char *src_c = (const char *)src;
volatile char *dest_c = (volatile char *)dest;
for (size_t i = 0; i < n; i++)
dest_c[i] = src_c[i];
return dest;
}
volatile void *memcpy_v(volatile void *dest, const volatile void *src, size_t n)
{
const volatile char *src_c = (const volatile char *)src;
volatile char *dest_c = (volatile char *)dest;
for (size_t i = 0; i < n; i++)
dest_c[i] = src_c[i];
return dest;
}
Редактировать:
Если мне понадобятся эти новые функции, учитывая, что я знаю, что никто не собирается изменять массив одновременно, имеет ли смысл использовать restrict
чтобы (возможно) помочь компилятору оптимизировать (если можно)?Возможно, так (поправьте меня, если я ошибаюсь):
volatile void *memcpy_v(restrict volatile void *dest,
const restrict volatile void *src,
size_t n)
{
const restrict volatile char *src_c = src;
restrict volatile char *dest_c = dest;
for (size_t i = 0; i < n; i++)
dest_c[i] = src_c[i];
return dest;
}
Редактировать 2 (добавить контекст):
void eusart_end_transmission (void)
{
reg_PIE1_TXIE_write(false); /* TXIE is TX interrupt enable */
eusart.tx.msg_len = 0;
eusart.tx.msg_posn = 0;
}
void eusart_tx_send_next_c (void)
{
uint16_t tmp;
if (data_9b) {
tmp = eusart.tx.buff.b9[eusart.tx.msg_posn++];
reg_TXSTA_TX9D_write(tmp >> 8);
TXREG = tmp;
} else {
TXREG = eusart.tx.buff.b8[eusart.tx.msg_posn++];
}
}
void __interrupt() isr(void)
{
if (reg_PIR1_TXIF_read()) {
if (eusart.tx.msg_posn >= eusart.tx.msg_len)
eusart_end_transmission();
else
eusart_tx_send_next_c();
}
}
Хотя volatile
может не быть необходимо необходимо (я задал это в другом вопросе: volatile для переменной, которая читается только в ISR? ) ,на этот вопрос все еще следует ответить в предположении, что volatile
необходим, чтобы будущие пользователи, которым действительно нужен volatile
(например, я, когда я реализую буфер RX), могли знать, что делать.
РЕДАКТИРОВАТЬ (Связано) (Jul / 19):
энергозависимый против памяти барьер для прерываний
В основном говорит, что volatile
не требуется, и поэтому эта проблема исчезает.