Appiko
hal_twim.c
1 
19 #include "hal_twim.h"
20 #include "stdbool.h"
21 #include "common_util.h"
22 
23 #if ISR_MANAGER == 1
24 #include "isr_manager.h"
25 #endif
26 
27 static struct {
28  uint32_t scl;
29  uint32_t sda;
30  twim_transfer_t current_transfer;
31  void (*handler)(twim_err_t err, twim_transfer_t transfer);
32  bool transfer_finished;
33  bool on;
34  uint8_t evt_mask;
35 }twim_status;
36 
40 #define TWIM_ID CONCAT_2(NRF_TWIM,TWIM_USED)
41 #define TWIM_IRQN TWIM_IRQN_a(TWIM_USED)
42 #define TWIM_IRQ_Handler TWIM_IRQ_Handler_a(TWIM_USED)
43 
44 #define TWIM_IRQN_a(n) TWIM_IRQN_b(n)
45 #define TWIM_IRQN_b(n) SPIM##n##_SPIS##n##_TWIM##n##_TWIS##n##_SPI##n##_TWI##n##_IRQn
46 
47 #define TWIM_IRQ_Handler_a(n) TWIM_IRQ_Handler_b(n)
48 #define TWIM_IRQ_Handler_b(n) SPIM##n##_SPIS##n##_TWIM##n##_TWIS##n##_SPI##n##_TWI##n##_IRQHandler
49 
51 #define TWIM_EVENT_CLEAR(x) do{ \
52  (x) = 0; \
53  (void) (x); \
54  }while(0)
55 
56 static inline void send_event(twim_transfer_t txfr)
57 {
58  //This works because the masks are 1 shifts of the transfer types
59  if(twim_status.evt_mask | (1 << txfr))
60  {
61  twim_status.handler(TWIM_ERR_NONE, twim_status.current_transfer);
62  }
63 }
64 
65 static void clear_all_events(void)
66 {
67  TWIM_ID->EVENTS_ERROR = 0;
68  TWIM_ID->EVENTS_LASTRX = 0;
69  TWIM_ID->EVENTS_LASTTX = 0;
70  TWIM_ID->EVENTS_RXSTARTED = 0;
71  TWIM_ID->EVENTS_STOPPED = 0;
72  TWIM_ID->EVENTS_SUSPENDED = 0;
73  TWIM_ID->EVENTS_TXSTARTED = 0;
74 }
75 
76 static void handle_error(void)
77 {
78 
79  TWIM_ID->INTENCLR = TWIM_INTENCLR_STOPPED_Msk;
80  (void)TWIM_ID->INTENCLR;
81 
82  twim_err_t err;
83  if((TWIM_ID->ERRORSRC & TWIM_ERRORSRC_ANACK_Msk))
84  {
85  TWIM_ID->ERRORSRC = TWIM_ERRORSRC_ANACK_Msk;
86  err = TWIM_ERR_ADRS_NACK;
87  }
88 
89  if((TWIM_ID->ERRORSRC & TWIM_ERRORSRC_DNACK_Msk))
90  {
91  TWIM_ID->ERRORSRC = TWIM_ERRORSRC_DNACK_Msk;
92  err = TWIM_ERR_DATA_NACK;
93  }
94  twim_status.handler(err, twim_status.current_transfer);
97  TWIM_ID->TASKS_STOP = 1;
98 }
99 
101 {
102  //Configure as strong drive low and disconnect high with pull-ups
103  NRF_GPIO->PIN_CNF[config->scl] =
104  ( (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
105  | (GPIO_PIN_CNF_DRIVE_H0D1 << GPIO_PIN_CNF_DRIVE_Pos)
106  | (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
107  | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
108  | (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos));
109  NRF_GPIO->PIN_CNF[config->sda] =
110  ( (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
111  | (GPIO_PIN_CNF_DRIVE_H0D1 << GPIO_PIN_CNF_DRIVE_Pos)
112  | (GPIO_PIN_CNF_PULL_Pullup << GPIO_PIN_CNF_PULL_Pos)
113  | (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos)
114  | (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos));
115 
116  twim_status.scl = TWIM_ID->PSEL.SCL = config->scl;
117  twim_status.sda = TWIM_ID->PSEL.SDA = config->sda;
118 
119  TWIM_ID->FREQUENCY = config->frequency;
120  TWIM_ID->ADDRESS = config->address;
121  //Use EasyDMA
122  TWIM_ID->TXD.LIST = TWIM_TXD_LIST_LIST_Msk;
123 
124  clear_all_events();
125  NVIC_ClearPendingIRQ(TWIM_IRQN);
126  NVIC_SetPriority(TWIM_IRQN, config->irq_priority);
127  NVIC_EnableIRQ(TWIM_IRQN);
128 
129  TWIM_ID->ENABLE = TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos;
130  twim_status.evt_mask = config->evt_mask;
131  twim_status.handler = config->evt_handler;
132  twim_status.transfer_finished = true;
133  twim_status.on = true;
134 }
135 
136 void hal_twim_uninit(void)
137 {
138  if(twim_status.on == false){
139  return;
140  }
141  twim_status.on = false;
142  TWIM_ID->ENABLE = TWIM_ENABLE_ENABLE_Disabled << TWIM_ENABLE_ENABLE_Pos;
143 
144  NVIC_ClearPendingIRQ(TWIM_IRQN);
145  NVIC_DisableIRQ(TWIM_IRQN);
146 
147  //Configure back to standard drive high and low
148  NRF_GPIO->PIN_CNF[twim_status.scl] =
149  ( (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
150  | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
151  | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
152  | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)
153  | (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos));
154  NRF_GPIO->PIN_CNF[twim_status.sda] =
155  ( (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
156  | (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)
157  | (GPIO_PIN_CNF_PULL_Disabled << GPIO_PIN_CNF_PULL_Pos)
158  | (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)
159  | (GPIO_PIN_CNF_DIR_Input << GPIO_PIN_CNF_DIR_Pos));
160 
161  TWIM_ID->ADDRESS = 0;
162 }
163 
164 static twim_ret_status initial_txfr_check(void){
165  if(twim_status.on == false)
166  {
167  return TWIM_UNINIT;
168  }
169 
170  //Starting from a previous transfer where TWIM_TRANSFER_DONE_MSK wasn't set
171  if(TWIM_ID->EVENTS_STOPPED)
172  {
173  twim_status.transfer_finished = true;
174  return TWIM_STARTED;
175  }
176 
177  if(twim_status.transfer_finished == false)
178  {
179  return TWIM_BUSY;
180  }
181  return TWIM_STARTED;
182 }
183 
184 twim_ret_status hal_twim_tx(uint8_t * tx_ptr, uint32_t tx_len)
185 {
186  twim_ret_status check_val = initial_txfr_check();
187  if(check_val != TWIM_STARTED)
188  {
189  return check_val;
190  }
191 
192  clear_all_events();
193  TWIM_ID->TXD.PTR = (uint32_t) tx_ptr;
194  TWIM_ID->TXD.MAXCNT = tx_len;
195  TWIM_ID->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
196  TWIM_ID->TASKS_STARTTX = 1;
197 
198  twim_status.current_transfer = TWIM_TX;
199  twim_status.transfer_finished = false;
200 
201  //Error is always set, as its required to recover from an error
202  TWIM_ID->INTEN = TWIM_INTENSET_ERROR_Msk;
203  (void)TWIM_ID->INTEN;
204  TWIM_ID->INTENSET = (twim_status.evt_mask & TWIM_TX_DONE_MSK)?
205  (TWIM_INTENSET_STOPPED_Msk) : 0 ;
206 
207  return TWIM_STARTED;
208 }
209 
210 twim_ret_status hal_twim_rx(uint8_t * rx_ptr, uint32_t rx_len)
211 {
212  twim_ret_status check_val = initial_txfr_check();
213  if(check_val != TWIM_STARTED)
214  {
215  return check_val;
216  }
217 
218  clear_all_events();
219  TWIM_ID->RXD.PTR = (uint32_t) rx_ptr;
220  TWIM_ID->RXD.MAXCNT = rx_len;
221  TWIM_ID->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
222  TWIM_ID->TASKS_STARTRX = 1;
223 
224  twim_status.current_transfer = TWIM_RX;
225  twim_status.transfer_finished = false;
226 
227  //Error is always set, as its required to recover from an error
228  TWIM_ID->INTEN = TWIM_INTENSET_ERROR_Msk;
229  (void)TWIM_ID->INTEN;
230  TWIM_ID->INTENSET = (twim_status.evt_mask & TWIM_RX_DONE_MSK)?
231  (TWIM_INTENSET_STOPPED_Msk) : 0 ;
232 
233  return TWIM_STARTED;
234 }
235 
236 twim_ret_status hal_twim_tx_rx(uint8_t * tx_ptr, uint32_t tx_len,
237  uint8_t * rx_ptr, uint32_t rx_len)
238 {
239  twim_ret_status check_val = initial_txfr_check();
240  if(check_val != TWIM_STARTED)
241  {
242  return check_val;
243  }
244 
245  clear_all_events();
246  TWIM_ID->TXD.PTR = (uint32_t) tx_ptr;
247  TWIM_ID->TXD.MAXCNT = tx_len;
248  TWIM_ID->RXD.PTR = (uint32_t) rx_ptr;
249  TWIM_ID->RXD.MAXCNT = rx_len;
250  TWIM_ID->SHORTS = TWIM_SHORTS_LASTTX_STARTRX_Msk |
251  TWIM_SHORTS_LASTRX_STOP_Msk;
252  TWIM_ID->TASKS_STARTTX = 1;
253 
254  twim_status.current_transfer = TWIM_TX_RX;
255  twim_status.transfer_finished = false;
256 
257  //Error is always set, as its required to recover from an error
258  TWIM_ID->INTEN = TWIM_INTENSET_ERROR_Msk;
259  (void)TWIM_ID->INTEN;
260  TWIM_ID->INTENSET = (twim_status.evt_mask & TWIM_TX_RX_DONE_MSK)?
261  (TWIM_INTENSET_STOPPED_Msk) : 0 ;
262 
263  return TWIM_STARTED;
264 }
265 
267 {
268  return TWIM_ID->ADDRESS;
269 }
270 #if ISR_MANAGER == 1
271 void hal_twim_Handler (void)
272 #else
273 void TWIM_IRQ_Handler(void)
274 #endif
275 {
276  if(TWIM_ID->EVENTS_ERROR == 1){
277 #if ISR_MANAGER == 0
278  TWIM_EVENT_CLEAR(TWIM_ID->EVENTS_ERROR);
279 #endif
280  handle_error();
281  }
282 
283  if(TWIM_ID->EVENTS_STOPPED == 1){
284 #if ISR_MANAGER == 0
285  TWIM_EVENT_CLEAR(TWIM_ID->EVENTS_STOPPED);
286 #endif
287  twim_status.transfer_finished = true;
288 
289  send_event(twim_status.current_transfer);
290  }
291 }
uint32_t irq_priority
Interrupt priority.
Definition: hal_twim.h:99
uint32_t evt_mask
Event Mask for specifying which successful transfer type calls the handler.
Definition: hal_twim.h:106
hal_twim_freq_t frequency
TWI frequency.
Definition: hal_twim.h:98
twim_ret_status hal_twim_rx(uint8_t *rx_ptr, uint32_t rx_len)
Start a Rx only TWI transfer.
Definition: hal_twim.c:210
The TWIM peripheral is not initialized.
Definition: hal_twim.h:80
The slave device generated an error on the data bytes.
Definition: hal_twim.h:72
twim_transfer_t
Defines for the types of transfers possible.
Definition: hal_twim.h:61
uint32_t hal_twim_get_current_adrs(void)
Get the current specified address of the I2C slave.
Definition: hal_twim.c:266
void(* evt_handler)(twim_err_t evt, twim_transfer_t transfer)
Definition: hal_twim.h:104
Only a Tx transfer.
Definition: hal_twim.h:62
The slave device generated an error on the address bytes.
Definition: hal_twim.h:71
void hal_twim_uninit(void)
Function for uninitializing and disabling one of the TWIM peripheral.
Definition: hal_twim.c:136
twim_err_t
Defines for the types of errors possible during TWI transactions.
Definition: hal_twim.h:69
A Tx transfer followed by Rx with a repeated start.
Definition: hal_twim.h:64
twim_ret_status
Defines for the return values for the transfer calls.
Definition: hal_twim.h:77
No error for this transfer.
Definition: hal_twim.h:70
Only a Rx transfer.
Definition: hal_twim.h:63
twim_ret_status hal_twim_tx_rx(uint8_t *tx_ptr, uint32_t tx_len, uint8_t *rx_ptr, uint32_t rx_len)
Start a Tx TWI transfer followed by a Rx by repeated start.
Definition: hal_twim.c:236
A transfer is already happening.
Definition: hal_twim.h:79
Transfer successfully started.
Definition: hal_twim.h:78
Structure for the TWI master driver initialization.
Definition: hal_twim.h:94
uint32_t scl
SCL pin number.
Definition: hal_twim.h:96
uint32_t sda
SDA pin number.
Definition: hal_twim.h:97
void hal_twim_init(hal_twim_init_config_t *config)
Function for initializing and enabling one of the TWIM peripheral.
Definition: hal_twim.c:100
twim_ret_status hal_twim_tx(uint8_t *tx_ptr, uint32_t tx_len)
Start a Tx only TWI transfer.
Definition: hal_twim.c:184